Classload

Java 運行時環境(JRE)是如何加載類的呢? 這玩意水太深,就談談一個類是怎麼加載的,jvm是怎麼工作的。


JVM啓動後有三個基本層次的類加載器,先上圖:


其中bootstap classloader 是c++寫的,jvm啓動後,它加載java環境運行所需要的一些class文件(java.* javax.*等包),例如rt.jar,但還有其他的,大家要想看的話可以測試下看加載了哪些:



Java代碼   收藏代碼
  1. URL [] urls=sun.misc.Launcher.getBootstrapClassPath().getURLs();  
  2.   
  3.      for (URL url:urls) {  
  4.               System.out.println(url.toExternalForm() );  
  5.      }  

 



extension classloader ,它負責加載JRE的擴展目錄(JAVA_HOME/jre/lib/ext或者由java.ext.dirs系統屬性指定的)中JAR的類包。


Application classloader(也有叫system classloader),就負責加載classpath裏面設置的類。


概念性的東西不多講,java的類加載都在java.lang.ClassLoader裏面.採用委託機制。


來看看在運行時需要一個類時是怎麼工作的吧。



1、jre需要一個類,假如爲a.class,會讓Application classloader找,


2、Application classloade 就委託他的上一層次加載器extension classloader找,


3、Extension classloader 委託他的上一層,讓bootstap classloader 找,


4、Bootstrap classloader是最高層的,他就親自找啊,但是沒有找到,於是就告訴Extension classloader說他這沒有,你自己找去。


5、Extesion classloader 就在ext目錄下找,發現也沒,於是說:“兄弟,哥這也沒,你自己找去。”


6、於是application classloader就在classpath裏面找,如果找到了返回給jre,如果沒有找到,就告訴jre沒找到,jre此時非常生氣:“李大爺的,逗了這麼大圈,居然沒有找到,靠:ClassNotFoundException ”


 JVM類加載機制(ClassLoader)源碼解析

其實JVM類加載機制,簡單地說就是類管理,也就是我們生成的class文件。
三個步驟:裝載(load)、鏈接(link)、解析(Resolve)、還有初始化(Initialize)
關於網上有很多講解加載的方式,和調用的方式,還是幾個基本的classLoader,這裏就不在多描述了。
這裏更多的是從源碼上來講解,達到理論結合實際。

首先是ClassLoader這個抽象類,這個是實現自定義類的基礎。那麼在調用的時候,首先都是調用loadClass這個方法,如圖:

該方法調用的是一個重載方法loadClass(name,false)方法如圖:

第一行註釋很清楚的表示,從當前的類加載器里加載class文件,如果已經加載,就直接返回;不存在就加載;如果返回NULL,表示class文件不在這個類加載器的範圍內。
這是就會執行if(c==null)下面的方法,就會去找父加載器(類加載器的整個結構是樹形的,這裏不多介紹),如果父加載器不存在,就直接找到根加載器(根加載器一定存在)。
我們現在開始解析每個被調用方法。
首先看下findLoadedClass方法,如圖:

該方法首先,校驗類名是否正確(類似於 test.demo.Test.class),校驗成功後,調用本地方法findLoadedClass0方法。

接着是parent.loadClass(name,false)方法,該方法是重複的,只是對象不同而已,就不介紹了。
接着是findBootstrapClassOrNull方法,如圖:

方式與findLoadedClass方法一樣,就是本地調用是findBootstrapClass方法。
如果此時類還不存在,有一個findClass方法的調用。代碼如圖:

這裏看上去覺得很奇怪,直接就拋一個空異常了,其實根據註釋所說,就是自定義的類加載器需要實現這種方法。
爲什麼要實現這個方法呢?原因就是有些場景會需要通過網絡傳輸的方式進行加載類(該類其實是在遠程服務器上的),在很多地方就會用到,例如遠程調用等技術。
具體的實現例子,在URLClassLoader裏就有體現,這裏暫不介紹。

最後是調用了resolveClass方法,代碼如圖:

這個方法很簡單,主要是重新進行解析類,該解析主要是對所有的屬性/方法調用是否存在、相應的權限(如private、public等)進行驗證。那麼爲什麼默認都是傳入false呢?
那是因爲是否需要更強的安全機制的檢測,
另一個情況是類加載是無序的,會導致類鏈接不成功。
這裏有個狀態位的控制,主要是可能有些場景需要更嚴格的對類進行驗證(目前我還沒有使用到這樣的場景)。

這裏要注意的是:
類爲什麼是樹形結構,主要就是安全,因爲類加載器都是從不同的目錄進行加載的(網上有介紹三個最基礎的類加載器Bootstrap ClassLoader\Extension ClassLoader\System ClassLoader加載的默認目錄,這裏不多介紹),所以用這種目錄的方式來進行權限管理,常用都是使用classpath系統變量,如果我想自己定義一個加載目錄,那麼就需要實現自己的類加載器,進行相應的權限管理。
另外這個抽象類,實現了只是從本地進行加載類的方式,如果需要進行遠程加載類,那麼也需要實現自己的類加載器。

這裏是類加載器的基本功能介紹,裏面還有如何加載native Library 方法、class文件/包名相關安全驗證等,在後續會繼續介紹。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章