1灵份、類的加載、連接和初始化
? ? ? 加載:查找并加載類的二進(jìn)制數(shù)據(jù)(字節(jié)碼文件)
? ? ? 連接:
? ? ? ? ? ? ?驗(yàn)證:確保被加載的類的正確性(手工生成class文件割捅,可
? ? ? ? ? ? ? ? ? ? ? ? 能不符合JVM標(biāo)準(zhǔn)規(guī)范)
? ? ? ? ? ? ?準(zhǔn)備:為類的靜態(tài)變量分配內(nèi)存何什,并將其初始化為默認(rèn)值
? ? ? ? ? ? ? ? ? ? ?(這個時候并不涉及new對象的操作)
? ? ? ? ? ? ?解析:把類中的符號引用轉(zhuǎn)換為直接引用(Child.run()轉(zhuǎn)
? ? ? ? ? ? ? ? ? ? ? ? 換為pointer)
? ? ? 初始化:為類的靜態(tài)變量賦予正確的初始值(正確:用戶賦予的
? ? ? ? ? ? ? ? ? ? 值)
? ? ? 示例:MyTest
? ?1.1類的使用方式:
? ? ? ? 所有的類或接口只有在java程序“首次主動使用”才會初始化
? ? ? ? 主動使用:
? ? ? ? ? ? ? ?(1)創(chuàng)建類的實(shí)例new Test();
? ? ? ? ? ? ? ?(2)訪問某個類的靜態(tài)變量,或者為某個類的靜態(tài)變量賦值:a =
? ? ? ? ? ? ? ? ? ? Test.b; Test.b = a;
? ? ? ? ? ? ? ?(3)調(diào)用類的靜態(tài)方法:Test.doSomething();
? ? ? ? ? ? ? ?(4)初始化一個類的子類:
? ? ? ? ? ? ? ? ? ? class Parent{};class Childextends Parent{
? ? ? ? ? ? ? ? ? ? public static int a= 3};
? ? ? ? ? ? ? ? ? ? Child.a =4;
? ? ? ? ? ? ? (5) java虛擬機(jī)就啟動時被標(biāo)明為啟動類的類(java TestTest
? ? ? ? ? ? ? ? ? ?中有main方法)
? ? ? ? ? ? ? (6)反射Class.forName(“Test”);
? ? ? ? ?除此之外的使用都是被動使用漫贞,被動使用是不會觸發(fā)初始化的。
? ?1.2類的加載:
? ? ? ? 將.class文件中的二進(jìn)制數(shù)據(jù)讀入到內(nèi)存中育叁,將其放在運(yùn)行時數(shù)據(jù)區(qū)的方法區(qū)內(nèi)绕辖,然后在堆區(qū)創(chuàng)建一個java.lang.Class對象,又 來封裝在方法區(qū)內(nèi)的數(shù)據(jù)結(jié)構(gòu)擂红,class對象是jvm在類加載時自動創(chuàng)建的仪际。
? 1.3類加載器
? ? ? ? Java虛擬機(jī)自帶的類加載器:
? ? ? ?根類加載器(Bootstrap)(c++ java代碼中無法獲取該類)
? ? ? ?擴(kuò)展類加載器(Extension)(java )
? ? ? ?系統(tǒng)類加載器(System)(也叫應(yīng)用加載器)
? ? ? ?用戶自定義的類加載器
? ?層次關(guān)系:
? ? ? ?根類加載器:用c++實(shí)現(xiàn)围小,并沒有集成java.lang.ClassLoader類
? ? ? ?擴(kuò)展類記載器:父類加載器為根類加載器,加載目錄jre\lib\ext树碱,如果把用戶創(chuàng)建的jar包放在這個目錄下肯适,則會有擴(kuò)展類加載器? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 進(jìn)行加載。
? ? ? ?系統(tǒng)類加載器:父類加載器為擴(kuò)展類加載器成榜,從環(huán)境變量classpath或系統(tǒng)屬性java.class.path所指定的目錄加載類框舔,是自定義? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?加載類的默認(rèn)父類加載器。
? ? ? (1)核心類庫由bootstrap加載器進(jìn)行加載赎婚,獲取到為空刘绣,其他都可獲取,例Test2
? ? ? (2)調(diào)用一個類的靜態(tài)變量挣输,且這個靜態(tài)變量在編譯時期就可以確定纬凤,則不會進(jìn)行類的初始化,當(dāng)這個類在執(zhí)行階段才能確? ? ? ? ? ? ? ? ? ? ?定撩嚼,則會進(jìn)行初始化停士,例Test4,Test3
? ? ? (3)加載順序—啟動類—父類—子類完丽,例Test5恋技,Test6
? ? ? (4)調(diào)用loadClass方法加載一個類,并不是對類的主動使用逻族,并不會初始化該類蜻底。例Test7
? 1.4類的加載機(jī)制—父委托機(jī)制
? ? ? (1)除了java自帶的根類加載器,其余類加載器都有且僅有一個父類加載器聘鳞,只有當(dāng)父類加載器不能進(jìn)行加載的時候薄辅,才會由? ? ? ? ? ? ? ? 子類加載器進(jìn)行加載。
? ? ? (2)父子加載器并非繼承關(guān)系搁痛,子加載器并不一定繼承父類加載器
? ? ? (3)安全考慮长搀,每個類都會有確定的層次中的一個類加載器進(jìn)行加載宇弛,自定義除外鸡典。
? ? ?(4)每個類都有自己的命名空間,命名空間由該類加載器及所有父類加載器所加載的類組成枪芒。同一個命名空間中彻况,不會存在兩? ? ? ? ? ? ? ? 個完整名字相同的類,不同命名空間中舅踪,可能存在完整名字相同的類纽甘。
? 1.5 用戶自定義類加載器
? ? ? ? 繼承ClassLoader類,覆蓋findClass方法抽碌,會由loadClass方法調(diào)用悍赢。
? ?1.6 JVM虛擬機(jī)執(zhí)行整體順序
2、JVM的內(nèi)存結(jié)構(gòu)
方法區(qū)和對是所有線程共享的內(nèi)存區(qū)域;而java棧左权、本地方法棧和程序員計(jì)數(shù)器是運(yùn)行是線程私有的內(nèi)存區(qū)域皮胡。
程序計(jì)數(shù)器:當(dāng)前線程所執(zhí)行字節(jié)碼指令的行號指示器,線程私有赏迟,執(zhí)行java方法屡贺,計(jì)數(shù)器記錄的是java代碼的行號,執(zhí)行native方法則為null(undefined)锌杀;
虛擬機(jī)棧:線程私有甩栈,表示java方法執(zhí)行的內(nèi)存模型,每個棧幀對應(yīng)的是一個調(diào)用的方法糕再,包括局部變量表量没、動態(tài)鏈接、操作數(shù)棧亿鲜、指向當(dāng)前方法所屬的類的運(yùn)行時常量池允蜈、方法返回地址和附加信息。
棧幀是保存在虛擬機(jī)棧中的蒿柳,棧幀是用來存儲數(shù)據(jù)和存儲部分過程結(jié)果的數(shù)據(jù)結(jié)構(gòu)饶套,同時也被用來處理動態(tài)鏈接(Dynamic Linking)、方法返回值和異常分派(Dispatch Exception)垒探。線程運(yùn)行過程中妓蛮,只有一個棧幀是處于活躍狀態(tài),稱為“當(dāng)前活躍棧幀”圾叼,當(dāng)前活動棧幀始終是虛擬機(jī)棧的棧頂元素蛤克。
局部變量表:方法參數(shù),方法內(nèi)部定義的變量夷蚊。
操作數(shù)棧:每一個方法的調(diào)用构挤,都會建立一個操作數(shù)棧
動態(tài)鏈接:與靜態(tài)解析對應(yīng),一部分的符號引用會在運(yùn)行是轉(zhuǎn)化為直接引用
方法返回地址:正常完成出口惕鼓,異常完成出口
本地方法棧:線程私有筋现,與虛擬機(jī)棧類似,為native方法服務(wù)
堆:線程共享箱歧,存儲對象本身及數(shù)組
方法區(qū):線程共享矾飞,類信息,靜態(tài)變量呀邢,常量洒沦,編譯后的代碼等
運(yùn)行時常量池:存放編譯期生成的各種字面量和符號引用
2.1對象的創(chuàng)建—內(nèi)存分配
(1)jvm遇到一個new指令,檢查這個指令的參數(shù)是否能在常量池中定位到一個類的符號引用价淌,然后檢查這個類代表的類是否已被加載申眼、解析和初始化瞒津。如果沒有,則進(jìn)行類加載括尸。
(2)類的加載—先加載父類仲智,先加載靜態(tài)塊,順序加載姻氨。
(3)JVM開始為對象分配內(nèi)存钓辆,兩種方式:
指針碰撞:java堆內(nèi)存規(guī)整,GC帶有壓縮整理功能
空閑列表:java堆內(nèi)存空間并不規(guī)整肴焊。
解決多線程分配內(nèi)存的問題:
失敗重試:保證更新操作的原子性
本地線程緩存惹傲:TLAB,在啟動線程時娶眷,就會在堆中給該線程預(yù)留一個內(nèi)存空間
(4)內(nèi)存分配完后似嗤,首先內(nèi)存空間都初始化為零值。若使用TLAB届宠,則在分配TLAB時即已初始化成功烁落。
(5)設(shè)置對象頭(Object Header):如何才能找到類的元數(shù)據(jù)信息,對象的哈希碼豌注,對象的GC分代年齡等信息伤塌。
(6)執(zhí)行方法,將對象初始化為指定的值轧铁。
3每聪、GC垃圾回收
3.1判斷對象失效
(1)引用計(jì)數(shù)算法
給對象添加一個引用計(jì)數(shù)器,每當(dāng)有一個地方引用它齿风,計(jì)數(shù)器值就加1药薯,當(dāng)引用失效就減1,計(jì)數(shù)器為0的對象就是不可能再被引用的對象救斑。
實(shí)現(xiàn)簡單童本,但無法解決循環(huán)引用的問題
(2)可達(dá)性分析算法
通過一系列成為“GC ROOT”的對象作為起始點(diǎn),向下搜索脸候,走過的路徑成為引用鏈穷娱,當(dāng)一個對象到GC ROOT沒有任何引用鏈相連時,則證明此對象是不可用的纪他。
可作為GC ROOT的對象有:
虛擬機(jī)棧中引用的對象
方法區(qū)中靜態(tài)屬性引用的對象
方法區(qū)中常量引用的對象
本地方法棧中JNI(native方法)引用的對象
3.2垃圾回收算法
(1)標(biāo)記—清除算法
先對不用的對象進(jìn)行標(biāo)記鄙煤,然后清除
(2)復(fù)制算法
將內(nèi)存按容量分為幾塊晾匠,在一個時間段只使用一塊內(nèi)存茶袒,當(dāng)這塊內(nèi)存用完,則將內(nèi)存清理干凈凉馆,然后將可用的對象復(fù)制到另一個區(qū)域薪寓。
(3)標(biāo)記—整理算法
復(fù)制算法在對象存活率較高時要進(jìn)行較多的復(fù)制操作亡资,效率將變低,而且浪費(fèi)空間向叉。
標(biāo)記—整理的標(biāo)記過程與標(biāo)記—清除一樣锥腻,但后續(xù)步驟不是對可回收對象進(jìn)行清理,而是讓所有存活的對象都向一端移動母谎,然后直接清理掉端邊界以外的內(nèi)存瘦黑。
(4)分代收集算法
根據(jù)對象存活周期的不同將內(nèi)存劃分為幾塊,一般把java堆分為新生代和老年代奇唤。
在新生代中每次垃圾收集時都發(fā)現(xiàn)有大批對象死去幸斥,只有少量存活,就選用復(fù)制算法咬扇,只需要付出少量存活對象的復(fù)制成本就可以完成收集甲葬。而老年代因?yàn)閷ο蟠婊盥矢悖瑳]有額外空間對它進(jìn)行分配擔(dān)保懈贺,就必須使用標(biāo)記—清理或標(biāo)記—整理算法來進(jìn)行回收