JAVA類加載機制(jdk8)

三句話總結JDK8的類加載機制:

  1. 類緩存:每個類加載器對他加載過的類都有一個緩存。
  2. 雙親委派:向上委托查找,向下委托加載。
  3. 沙箱保護機制:不允許應用程序加載JDK內部的系統類。

JDK8的類加載體系

在這里插入圖片描述
類加載器的核心方法

//protected聲明可以被子類覆蓋
protected Class<?> loadClass(String name, boolean resolve)throws ClassNotFoundException{synchronized (getClassLoadingLock(name)) {// 每個類加載起對他加載過的類都有一個緩存,先去緩存中查看有沒有加載過Class<?> c = findLoadedClass(name);if (c == null) {//沒有加載過,就走雙親委派,找父類加載器進行加載。long t0 = System.nanoTime();try {if (parent != null) {c = parent.loadClass(name, false);} else {c = findBootstrapClassOrNull(name);//沒有父類加載器(表示parent是BootstrapClassLoad)就查詢BootstrapClassLoad的緩存不存在則創建,但是BootstrapClassLoad只創建核心類庫(java.lang.*、java.util.* 等,非核心類會返回空或者或拋異常)}} catch (ClassNotFoundException e) {}if (c == null) {//BootstrapClassLoaderlong t1 = System.nanoTime();// 父類加載起沒有加載過,就自行解析class文件加載。c = findClass(name);sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);sun.misc.PerfCounter.getFindClasses().increment();}}//這一段就是加載過程中的鏈接Linking部分,分為驗證、準備,解析三個部分。// 運行時加載類,默認是無法進行鏈接步驟的。if (resolve) {resolveClass(c);}return c;}}

可以保護JDK內部的核心類不會被應用覆蓋,因為本加載器緩存沒有就會往父類加載器緩存找,核心類在bootstarp加載器緩存中存在。并且緩存相關代碼是native無法直接被java代碼操作
在這里插入圖片描述

沙箱保護機制

private ProtectionDomain preDefineClass(String name,ProtectionDomain pd){if (!checkName(name))throw new NoClassDefFoundError("IllegalName: " + name);// 不允許加載核心類if ((name != null) && name.startsWith("java.")) {throw new SecurityException("Prohibited package name: " +name.substring(0, name.lastIndexOf('.')));}if (pd == null) {pd = defaultDomain;}if (name != null) checkCerts(name, pd.getCodeSource());return pd;}

Linking鏈接過程

在ClassLoader的loadClass方法中還有一個resolveClass,是一個native方法,其實現的過程稱為linking-鏈接。
在這里插入圖片描述

  • 加載:通過類加載器將class文件加載到jvm中(應用唯一可插手的步驟)
  • 驗證:驗證class規范
  • 準備:為靜態變量分配內存賦默認值(例如int為0)
  • 解析:符號引用解析為直接引用
  • 初始化:靜態變量賦值,執行類的靜態代碼塊,初始化當前類的父類

Class.forName默認會直接進行Linking并初始化,ClassLoader.loadClass不會而是只加載(懶加載)

通過類加載器引入外部Jar包

public class OADemo2 {public static void main(String[] args) throws Exception {Double salary = 15000.00;Double money = 0.00;URL jarPath = new URL("file:/Users/roykingw/DevCode/ClassLoadDemo/out/artifacts/SalaryCaler_jar/SalaryCaler.jar");URLClassLoader urlClassLoader = new URLClassLoader(new URL[] {jarPath});//模擬不停機狀態while (true) {try {money = calSalary(salary,urlClassLoader);System.out.println("實際到手Money:" + money);}catch(Exception e) {e.printStackTrace();System.out.println("加載出現異常 :"+e.getMessage());}Thread.sleep(5000);}}private static Double calSalary(Double salary,ClassLoader classloader) throws Exception {Class<?> clazz = classloader.loadClass("com.roy.oa.SalaryCaler");if(null != clazz) {Object object = clazz.newInstance();return (Double)clazz.getMethod("cal", Double.class).invoke(object, salary);}return -1.00;}
}
  1. 哪些jar包適合放到外部加載?
    規則引擎、統一審批規則、訂單狀態規則,因為會直接加載到jvm,要考慮安全性

  2. 外部jar包可以放到哪些地方?
    URLClassLoader可以定義URL從遠程Web服務器加載Jar包。
    drools規則引擎實現了從maven倉庫遠程加載核心規則文件。

自定義類加載器實現Class代碼混淆

對class進行MD5、對稱加密、非對稱加密,類加載器再進行解密。同時把解密類加載器通過另一個加載器內網遠程加載,或者u盤加載來實現Class代碼混淆加密

自定義類加載器實現熱加載

在 Java 中,類加載器一旦加載某個類,該類就無法被“卸載”(除非整個類加載器被 GC 回收)。所以熱加載的關鍵是:每次都使用一個新的 ClassLoader 實例來加載 class 文件,不復用舊的 ClassLoader,否則類會復用舊版本

public class OADemo5 {public static void main(String[] args) throws Exception {Double salary = 15000.00;Double money = 0.00;//模擬不停機狀態while (true) {try {money = calSalary(salary);System.out.println("實際到手Money:" + money);}catch(Exception e) {System.out.println("加載出現異常 :"+e.getMessage());}Thread.sleep(5000);}}private static Double calSalary(Double salary) throws Exception {SalaryJARLoader salaryClassLoader = new SalaryJARLoader("/Users/roykingw/lib/SalaryCaler.jar");//每次都創建新的ClassLoader實例來加載 class 文件,新的ClassLoader里當然沒有緩存System.out.println(salaryClassLoader.getParent());Class<?> clazz = salaryClassLoader.loadClass("com.roy.oa.SalaryCaler");if(null != clazz) {Object object = clazz.newInstance();return (Double)clazz.getMethod("cal", Double.class).invoke(object, salary);}return -1.00;}
}

這種熱加載機制需要創建出非常多的ClassLoader對象。而這些不用的ClassLoader對象加載過的緩存對象也會隨之成為垃圾。這會讓JVM中本來就不大的元數據區帶來很大的壓力,極大的增加GC線程的壓力。

把SalaryJARLoader加載過的類打印出來,你會發現,在加載SalaryCaler時,其實不光加載了這個類,同時還加載了Double和Object兩個類。這兩個類哪里來的?這就是JVM實現的懶加載機制。JVM為了提高類加載的速度,并不是在啟動時直接把進程當中所有的類一次加載完成,而是在用到的時候才去加載。也就是懶加載。

打破雙親委派,實現同類多版本共存

在這里插入圖片描述
外部加載的jar包,使用自定義加載器,這時如果代碼中使用一樣的類,那么AppClassLoader就有了緩存,就不會走自定義加載器的邏輯了

tomcat打破雙親委派
在這里插入圖片描述
tomcat的幾個主要類加載器:

  • commonLoader:Tomcat最基本的類加載器,加載路徑中的class可以被Tomcat容器本身以及各個Webapp訪問;
  • catalinaLoader:Tomcat容器私有的類加載器,加載路徑中的class對于Webapp不可見;
  • sharedLoader:各個Webapp共享的類加載器,加載路徑中的class對于所有Webapp可見,但是對于Tomcat容器不可見;
  • WebappClassLoader:各個Webapp私有的類加載器,加載路徑中的class只對當前Webapp可見,比如加載war包里相關的類,每個war包應用都有自己的WebappClassLoader,實現相互隔離,比如不同war包應用引入了不同的spring版本,這樣實現就能加載各自的spring版本;
  • Jsp類加載器:針對每個JSP頁面創建一個加載器。這個加載器比較輕量級,所以Tomcat還實現了熱加載,也就是JSP只要修改了,就創建一個新的加載器,從而實現了JSP頁面的熱更新。

使用類加載器能不能不用反射?

強行的類型轉換會報錯Exception in thread “main” java.lang.ClassCastException: com.roy.oa.SalaryCaler cannot be cast to com.roy.oa.SalaryCaler

JDK的SPI擴展機制
ServiceLoader.load(SalaryCalService.class) 就可以查找到某一個接口的全部實現類。應用所需要的,是提供一個配置文件。 這個配置文件需要放在 ${classpath}/META-INF/services這個固定的目錄下。然后文件名是傳入接口的全類名。而文件的內容則是一行表示一個實現類的全類名。

SPI機制也是傳入了ClassLoader的。

public static <S> ServiceLoader<S> load(Class<S> service) {ClassLoader cl = Thread.currentThread().getContextClassLoader();return ServiceLoader.load(service, cl);}

SPI配置文件可以放入jar包中,但是必須要使用URLClassLoader,由于向上委托查找會找到AppClassLoader,那么我們可以通過構造函數設置它的parent為salaryJARLoader就可以實現了。還可以通過java -cp這個啟動參數直接把jar包導入

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/pingmian/85006.shtml
繁體地址,請注明出處:http://hk.pswp.cn/pingmian/85006.shtml
英文地址,請注明出處:http://en.pswp.cn/pingmian/85006.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

更進一步深入的研究ObRegisterCallBack

引入 我們如果想hook對象的回調,在上篇文章里我們已經知道了對象回調函數存在一個列表里面&#xff0c;我們通過dt可以看見&#xff0c;這里他是一個LIST_ENTRY結構&#xff0c;但是實際調用的時候&#xff0c;這樣是行不通的&#xff0c;說明它結構不對 0: kd> dt _OBJEC…

Nginx-3 Nginx 的負載均衡策略

Nginx-3 Nginx 的負載均衡策略 Nginx 的負載均衡其實就是指將請求按照一定的策略轉發給服務集群中的一臺&#xff0c;提高了服務集群的可用性&#xff0c;解決數據流量過大、網絡負荷過重的問題。 AKF 擴展立方體 分為 3 個方向負載&#xff1a; x 軸&#xff1a;增加實例數…

Wiiu平臺RetroArch全能模擬器美化整合包v1.18

這款WiiU平臺RetroArch全能模擬器美化整合包v1.18的亮點包括&#xff1a; 1. 18款平臺完美兼容&#xff1a;無論你是喜歡NES時代的經典游戲&#xff0c;還是鐘愛SNES、GBA等平臺的大作&#xff0c;這款整合包都能滿足你的需求&#xff0c;讓你盡情暢玩游戲。 2. 三款自制主題&a…

MyBatis原理

Mybatis執行過程為&#xff1a;接口代理->sqlSession會話->executor執行器->JDBC操作 一、接口代理 Mybatis根據Mapper接口&#xff0c;動態生成相應實現類 二、SqlSession介紹 MyBatis核心對象SqlSession介紹 - MyBatis中文官網 三、Executor執行器介紹 精通My…

升級內核4.19-腳本

#bash cd /root yum remove -y kernel-tools-3.10.0-1160.el7.x86_64 yum remove -y kernel-tools-libs-3.10.0-1160.el7.x86_64tar -xvf rhel-7-amd64-rpms.tar.gz cd /root/rhel-7-amd64-rpms #安裝依賴、包括socat&conntrack yum localinstall -y *.rpm --skip-broken#升…

全面理解 JVM 垃圾回收(GC)機制:原理、流程與實踐

JVM 的 GC&#xff08;Garbage Collection&#xff09;機制是 Java 程序性能的關鍵支柱。本文將從堆內存布局、回收原理、GC 算法、流程細節、并發收集器機制等維度&#xff0c;系統講清楚 GC 的底層運作原理和優化思路。 一、JVM 堆內存結構 Java 堆是 GC 管理的主要區域&am…

runas命令讓其他用戶以管理員權限運行程序

RUNAS 用法: RUNAS使用示例&#xff1a; runas /noprofile /user:mymachine\administrator cmd #本機Administrator管理員身份執行CMD&#xff0c;/noprofile為不加載該用戶的配置信息。runas /profile /env /user:mydomain\admin “mmc %windir%\system32\dsa.msc” #本機上…

實戰指南:部署MinerU多模態文檔解析API與Dify深度集成(實現解析PDF/JPG/PNG)

MinerU web api部署 MinerU 能夠將包含圖片、公式、表格等元素的多模態 PDF、PPT、DOCX 等文檔轉化為易于分析的 Markdown 格式。 克隆 MinerU 的倉庫 git clone https://github.com/opendatalab/MinerU.gitcd 到 projects/web-api cd projects/web-api在可以科學上網的情況下…

向量外積與秩1矩陣的關系

向量外積與秩1矩陣的關系 flyfish 向量外積是構造秩1矩陣的基本工具&#xff0c;其本質是用兩組向量的線性組合刻畫矩陣的行和列相關性&#xff1b;任意秩1矩陣必可表示為外積&#xff0c;而低秩矩陣&#xff08;秩 k k k&#xff09;可分解為 k k k 個外積矩陣的和&#x…

設計模式-創建型模式(詳解)

創建型模式 單例模式 一個類只允許創建一個對象&#xff0c;稱為單例。 單例體現&#xff1a;配置類、連接池、全局計數器、id生成器、日志對象。 懶漢式 (線程不安全) 單例&#xff1a;【不可用】 用到該單例對象的時候再創建。但存在很大問題&#xff0c;單線程下這段代…

什么是BI?有哪些應用場景

BI&#xff08;Business Intelligence&#xff0c;商業智能&#xff09;是通過技術手段對海量業務數據進行采集、整合、分析和可視化的過程&#xff0c;旨在幫助企業從數據中獲取洞察&#xff0c;支持決策。其核心是通過工具&#xff08;如Quick BI&#xff09;將原始數據轉化為…

從零開始:使用Vite和Vue.js搭建一個空項目

進入node.js官網 https://nodejs.org/zh-cn 下載node.js 點擊進行安裝&#xff0c; 完成之后&#xff0c;按住shift鼠標右鍵&#xff0c;打開powershell窗口 輸入node -v &#xff0c;出現版本號就是成功了 node -v 接下來&#xff0c;打開設置&#xff0c;搜索開發者設置&…

Redis 核心數據類型及典型使用場景詳解

在日常開發中&#xff0c;Redis 不僅是緩存利器&#xff0c;更是一套高性能的數據結構服務。你是否真的了解 Redis 提供的五種核心數據類型&#xff1f;它們各自的底層結構和適用場景又有哪些差異&#xff1f;本篇博客將深入解析 Redis 的數據類型及其典型應用&#xff0c;助你…

threejs webVR獲取相機正前方向量

通常獲取相機正前方可以使用camera.getWorldDirection(new Vector3()) 函數來得到&#xff0c;但是在threejs0.139.2版本中進入VR后使用上面函數獲取的數據是固定不變的&#xff0c;不管是否旋轉了頭盔&#xff0c;經過一番研究發現必須使用renderer.xr.getCamera() 此函數獲取…

華為OD-2024年E卷-字符統計及重排[100分] -- python

問題描述&#xff1a; 給出一個僅包含字母的字符串&#xff0c;不包含空格&#xff0c;統計字符串中各個字母(區分大小寫)出現的次數&#xff0c;并按照字母出現次數從大到小的順序輸出各個字母及其出現次數。如果次數相同&#xff0c;按照自然順序進行排序&#xff0c;且小寫…

MCP(模型上下文協議)協議和Http協議對比

MCP&#xff08;Model Context Protocol&#xff0c;模型上下文協議&#xff09;和 HTTP&#xff08;HyperText Transfer Protocol&#xff0c;超文本傳輸協議&#xff09;是兩種定位完全不同的協議&#xff0c;主要區別如下&#xff1a; 1. 核心定位 HTTP 通用網絡通信協議&am…

C++打印乘法口訣表

int main()??&#xff1a; 這是C 程序的入口點。每個C 程序都必須有一個 main 函數&#xff0c;程序從這里開始執行。 ??外層 for 循環??&#xff1a; for (int i 1; i < 10; i) { int i 1&#xff1a;定義并初始化循環變量 i 為 1。這里的 i 代表乘法表中的行…

RoGBAG 與 MCAP

RoGBAG 和 MCAP 都是機器人領域常用的二進制數據格式&#xff0c;用于存儲傳感器數據、控制命令和狀態信息。兩者主要區別在于&#xff1a; RoGBAG&#xff1a;ROS 1/2 的標準日志格式&#xff0c;采用 LZF/LZ4 壓縮&#xff0c;適合中小型數據集 MCAP&#xff1a;新一代機器人…

Ubuntu 空間占用情況排查常用命令

查看當前目錄總大小及子目錄占用詳情 du -sh * | sort -hr ??du??&#xff1a;磁盤使用統計命令??-s??&#xff1a;顯示每個參數的總計&#xff08;不遞歸子目錄&#xff09;??-h??&#xff1a;以人類可讀格式&#xff08;KB/MB/GB&#xff09;顯示??*??&…