一. 類的加載過程随常,Person person = new Person();為例進行說明。
1萄涯、因為new用到了Person.class绪氛,所以會先找到Person.class文件,并加載到內(nèi)存中涝影。
2枣察、執(zhí)行該類中的static代碼塊(如果有),給Person.class進行初始化
3燃逻、在堆內(nèi)存中開辟空間并分配內(nèi)存地址
4序目、在堆內(nèi)存中建立對象的特有屬性,并進行默認初始化
5伯襟、對對象的特有屬性進行顯示初始化
6猿涨、對對象進行構造代碼塊初始化
7、對對象構造函數(shù)進行初始化
8姆怪、在棧內(nèi)存中聲明Person類型的變量person
9叛赚、把堆內(nèi)存中的地址(引用)賦值給棧內(nèi)存中的person變量
二.JVM相關知識澡绩,GC機制。
JVM基本構成
從上圖可知俺附,JVM主要包括四個部分:
??1.類加載器(ClassLoader):在JVM啟動時或者在類運行時將需要的class加載到JVM中肥卡。(下圖表示了從java源文件到JVM的整個過程,可配合理解昙读。
??2.執(zhí)行引擎:負責執(zhí)行class文件中包含的字節(jié)碼指令召调;
??3.內(nèi)存區(qū)(也叫運行時數(shù)據(jù)區(qū)):是在JVM運行的時候操作所分配的內(nèi)存區(qū)。運行時內(nèi)存區(qū)主要可以劃分為5個區(qū)域蛮浑,如圖:
- 方法區(qū)(Method Area):用于存儲類結構信息的地方唠叛,包括常量池、靜態(tài)變量沮稚、構造函數(shù)等艺沼。雖然JVM規(guī)范把方法區(qū)描述為堆的一個邏輯部分, 但它卻有個別名non-heap(非堆)蕴掏,所以大家不要搞混淆了障般。方法區(qū)還包含一個運行時常量池。
- java堆(Heap):存儲java實例或者對象的地方盛杰。這塊是GC的主要區(qū)域挽荡。從存儲的內(nèi)容我們可以很容易知道,方法區(qū)和堆是被所有java線程共享的即供。
- java棧(Stack):java椂猓總是和線程關聯(lián)在一起,每當創(chuàng)建一個線程時逗嫡,JVM就會為這個線程創(chuàng)建一個對應的java棧青自。在這個java棧中又會包含多個棧幀,每運行一個方法就創(chuàng)建一個棧幀驱证,用于存儲局部變量表延窜、操作棧、方法返回值等抹锄。每一個方法從調(diào)用直至執(zhí)行完成的過程逆瑞,就對應一個棧幀在java棧中入棧到出棧的過程。所以java棧是現(xiàn)成私有的伙单。
- 程序計數(shù)器(PC Register):用于保存當前線程執(zhí)行的內(nèi)存地址呆万。由于JVM程序是多線程執(zhí)行的(線程輪流切換),所以為了保證線程切換回來后车份,還能恢復到原先狀態(tài),就需要一個獨立的計數(shù)器牡彻,記錄之前中斷的地方扫沼,可見程序計數(shù)器也是線程私有的出爹。
- 本地方法棧(Native Method Stack):和java棧的作用差不多,只不過是為JVM使用到的native方法服務的缎除。
????4.本地方法接口:主要是調(diào)用C或C++實現(xiàn)的本地方法及返回結果严就。
GC機制
??垃圾收集器一般必須完成兩件事:檢測出垃圾;回收垃圾器罐。怎么檢測出垃圾梢为?一般有以下幾種方法:
引用計數(shù)法:
??給對象添加引用計數(shù)器,每當有個地方引用對象轰坊,就給計數(shù)器 +1铸董,引用失效就 -1。好了肴沫,問題來了粟害,如果我們有兩個對象A和B,互相引用颤芬,除此之外悲幅,沒有其他任何對象引用他們,實際上這兩個對象已經(jīng)無法訪問站蝠,也就是我們說的垃圾對象汰具,但是互相引用,計數(shù)不為零菱魔,導致無法回收留荔,所以還有另外一種方法->
可達性分析算法:
??以根集對象為起點進行搜索,如果對象不可達豌习,即是垃圾對象存谎,這里的根集一般包括java棧中引用的對象、方法區(qū)常量池中引用的對象肥隆、本地方法中引用的對象等既荚。
??總之,JVM在做垃圾回收的時候栋艳,會檢查堆中的所有對象是否會被這些根集對象引用恰聘,不能夠被引用的對象就會被垃圾收集器回收。一般回收算法也有如下幾種:
1).標記-清除(Mark-sweep)
2).復制(Copying
3).標記-整理(Mark-Compact)
4).分代收集算法
具體的解釋可以參考本篇文章還不點我?
三. 類的加載器吸占,雙親機制晴叨,Android的類加載器。
類的加載器
??大家都知道矾屯,當我們寫好一個Java程序之后兼蕊,不是管是CS還是BS應用,都是由若干個.class文件組織而成的一個完整的Java應用程序件蚕,當程序在運行時孙技,即會調(diào)用該程序的一個入口函數(shù)來調(diào)用系統(tǒng)的相關功能产禾,而這些功能都被封裝在不同的class文件當中,所以經(jīng)常要從這個class文件中要調(diào)用另外一個class文件中的方法牵啦,如果另外一個文件不存在的亚情,則會引發(fā)系統(tǒng)異常。而程序在啟動的時候哈雏,并不會一次性加載程序所要用的所有class文件楞件,而是根據(jù)程序的需要,通過Java的類加載機制(ClassLoader)來動態(tài)加載某個class文件到內(nèi)存當中的裳瘪,從而只有class文件被載入到了內(nèi)存之后土浸,才能被其它class所引用。所以ClassLoader就是用來動態(tài)加載class文件到內(nèi)存當中用的盹愚。
雙親機制
1栅迄、原理介紹
??ClassLoader使用的是雙親委托模型來搜索類的,每個ClassLoader實例都有一個父類加載器的引用(不是繼承的關系皆怕,是一個包含的關系)毅舆,虛擬機內(nèi)置的類加載器(Bootstrap ClassLoader)本身沒有父類加載器,但可以用作其它ClassLoader實例的的父類加載器愈腾。當一個ClassLoader實例需要加載某個類時憋活,它會試圖親自搜索某個類之前,先把這個任務委托給它的父類加載器虱黄,這個過程是由上至下依次檢查的悦即,首先由最頂層的類加載器Bootstrap ClassLoader試圖加載,如果沒加載到橱乱,則把任務轉(zhuǎn)交給Extension ClassLoader試圖加載辜梳,如果也沒加載到,則轉(zhuǎn)交給App ClassLoader 進行加載泳叠,如果它也沒有加載得到的話作瞄,則返回給委托的發(fā)起者,由它到指定的文件系統(tǒng)或網(wǎng)絡等URL中加載該類危纫。如果它們都沒有加載到這個類時宗挥,則拋出ClassNotFoundException異常。否則將這個找到的類生成一個類的定義种蝶,并將它加載到內(nèi)存當中契耿,最后返回這個類在內(nèi)存中的Class實例對象。
2螃征、為什么要使用雙親委托這種模型呢搪桂?
??因為這樣可以避免重復加載,當父親已經(jīng)加載了該類的時候盯滚,就沒有必要子ClassLoader再加載一次锅棕∽驹螅考慮到安全因素,我們試想一下裸燎,如果不使用這種委托模式,那我們就可以隨時使用自定義的String來動態(tài)替代java核心api中定義的類型泼疑,這樣會存在非常大的安全隱患德绿,而雙親委托的方式,就可以避免這種情況退渗,因為String已經(jīng)在啟動時就被引導類加載器(Bootstrcp ClassLoader)加載移稳,所以用戶自定義的ClassLoader永遠也無法加載一個自己寫的String,除非你改變JDK中ClassLoader搜索類的默認算法会油。
3个粱、但是JVM在搜索類的時候,又是如何判定兩個class是相同的呢翻翩?
??JVM在判定兩個class是否相同時都许,不僅要判斷兩個類名是否相同,而且要判斷是否由同一個類加載器實例加載的嫂冻。只有兩者同時滿足的情況下胶征,JVM才認為這兩個class是相同的。就算兩個class是同一份class字節(jié)碼桨仿,如果被兩個不同的ClassLoader實例所加載睛低,JVM也會認為它們是兩個不同class。比如網(wǎng)絡上的一個Java類org.classloader.simple.NetClassLoaderSimple服傍,javac編譯之后生成字節(jié)碼文件NetClassLoaderSimple.class钱雷,ClassLoaderA和ClassLoaderB這兩個類加載器并讀取了NetClassLoaderSimple.class文件,并分別定義出了java.lang.Class實例來表示這個類吹零,對于JVM來說罩抗,它們是兩個不同的實例對象,但它們確實是同一份字節(jié)碼文件瘪校,如果試圖將這個Class實例生成具體的對象進行轉(zhuǎn)換時澄暮,就會拋運行時異常java.lang.ClassCaseException,提示這是兩個不同的類型阱扬。
Android類加載器
??對于Android而言泣懊,最終的apk文件包含的是dex類型的文件,dex文件是將class文件重新打包麻惶,打包的規(guī)則又不是簡單地壓縮馍刮,而是完全對class文件內(nèi)部的各種函數(shù)表,變量表進行優(yōu)化窃蹋,產(chǎn)生一個新的文件卡啰,即dex文件静稻。因此加載這種特殊的Class文件就需要特殊的類加載器DexClassLoader。