生命周期
當(dāng)啟動(dòng)一個(gè)Java程序時(shí)片部,一個(gè)JVM實(shí)例就產(chǎn)生了蓄诽,任何一個(gè)擁有public static void main(String[] args)函數(shù)的class都可以作為JVM實(shí)例運(yùn)行的起點(diǎn)
main()作為該程序初始線程的起點(diǎn)孟岛,任何其他線程均由該線程啟動(dòng)磕昼。JVM內(nèi)部有兩種線程:守護(hù)線程和非守護(hù)線程俺驶,main()屬于非守護(hù)線程介汹,守護(hù)線程通常由JVM自己使用纤房,java程序也可以標(biāo)明自己創(chuàng)建的線程是守護(hù)線程纵隔。
當(dāng)程序中的所有非守護(hù)線程都終止時(shí),JVM才退出炮姨;若安全管理器允許捌刮,程 序也可以使用Runtime類或者System.exit()來退出。
結(jié)構(gòu)
一舒岸、類加載器
雙親委派模式:防止其他類加載器加載混有惡意代碼的同名類绅作,同時(shí)也避免同一個(gè)類被多次重復(fù)加載
加載過程:
1.裝載:加載二進(jìn)制字節(jié)碼到JVM,類名+包名+ClassLoader 生成一個(gè)唯一的實(shí)例ID
2.鏈接:對(duì)二進(jìn)制字節(jié)碼進(jìn)行校驗(yàn)蛾派,解析類中調(diào)用的接口俄认,類
3.初始化,父類的靜態(tài)代碼塊碍脏、父類的靜態(tài)屬性——>子類的靜態(tài)代碼塊梭依、子類的靜態(tài)屬性——>父類的初始化代碼塊、父類的實(shí)例屬性——>父類的構(gòu)造方法——>子類的初始化代碼塊典尾、子類的實(shí)例屬性——>子類的構(gòu)造方法役拴。
分類:
1.Bootstrap ClassLoader,JVM的跟classLoader
2.Extension ClassLoader钾埂,加載擴(kuò)展功能的jar包
3.System ClassLoader(JDK中叫AppClassLoader)河闰,加載啟動(dòng)參數(shù)中指定Classpath中的jar包及目錄
4.User-Defined ClassLoader,開發(fā)人員繼承ClassLoader抽象類自行實(shí)現(xiàn)的ClassLoader
二褥紫、執(zhí)行引擎
JVM通過執(zhí)行引擎來完成字節(jié)碼的執(zhí)行姜性,在執(zhí)行過程中JVM采用的是自己的一套指令系統(tǒng)
編譯執(zhí)行:代碼完全編譯成機(jī)器碼,直接交由機(jī)器執(zhí)行
解釋執(zhí)行:代碼僅僅編譯成class文件髓考,沒有轉(zhuǎn)化為機(jī)器碼部念,虛擬機(jī)讀取一行代碼,轉(zhuǎn)化成一行機(jī)器碼氨菇,然后執(zhí)行
HotspotJVM是混合模式儡炼,大部分是解釋執(zhí)行,但是對(duì)部分熱點(diǎn)代碼會(huì)JIT(just-in-time)編譯器查蓉,也是我們常說的動(dòng)態(tài)編譯器乌询,編譯成機(jī)器碼,此處是編譯執(zhí)行豌研。
運(yùn)行模式可以通過jvm參數(shù)來指定妹田。不同平臺(tái)有不同的解釋器唬党,這是java可以跨平臺(tái)的基礎(chǔ),同時(shí)只要符合字節(jié)碼規(guī)則鬼佣,其他例如Scala驶拱、Groovy語言也可以在jvm平臺(tái)上運(yùn)行。
垃圾回收
一沮趣、分代思想
根據(jù)對(duì)象的生存周期屯烦,引用情況,大小等因素房铭,將堆內(nèi)存分成年輕代驻龟,老年代,不同區(qū)域采用不同的垃圾回收算法/垃圾回收器缸匪,盡可能縮短GC造成的應(yīng)該暫停翁狐,即STW
JVM 年輕代到年老代的晉升過程的判斷條件是什么呢?部分對(duì)象會(huì)在From和To區(qū)域中復(fù)制來復(fù)制去,如此交換15次(由JVM參數(shù)MaxTenuringThreshold決定,這個(gè)參數(shù)默認(rèn)是15),最終如果還是存活,就存入到老年代凌蔬;hotspot虛擬機(jī)露懒,server模式:如果某次分配內(nèi)存的時(shí)候,新生代內(nèi)存不足時(shí)會(huì)進(jìn)行內(nèi)存分配擔(dān)保
小點(diǎn):在Serial+Serial Old的GC策略下砂心,會(huì)先進(jìn)行一次minorGC(即YGC)懈词,如果還是放不下就會(huì)把新生代的存活的對(duì)象搬到年老代,然后新生代騰出來的空間用于為分配給最新的對(duì)象辩诞;
在Parallel Scavenge+Serial Old的GC策略下坎弯,先判斷要分配的內(nèi)存是不是>=Eden區(qū)大小的一半,如果是那么直接把該對(duì)象放入老生代译暂,否則才會(huì)做MinorGC然后判斷是否移動(dòng)新生代數(shù)據(jù)到老年代抠忘。
二、掃描/標(biāo)記
如何確定一個(gè)對(duì)象是否被引用外永?
1.引用計(jì)數(shù)器
引用計(jì)數(shù)的方法簡(jiǎn)單來說就是對(duì)每一個(gè)對(duì)象都提供一個(gè)關(guān)聯(lián)的引用計(jì)數(shù)崎脉,以此來標(biāo)識(shí)該對(duì)象是否被使用,當(dāng)這個(gè)計(jì)數(shù)為零時(shí)伯顶,說明這個(gè)對(duì)象已經(jīng)不再被使用了囚灼。
優(yōu)點(diǎn): 引用計(jì)數(shù)的好處是可以不用暫停應(yīng)用,當(dāng)計(jì)數(shù)變?yōu)榱銜r(shí)祭衩,即可將此對(duì)象的內(nèi)存空間回收
缺點(diǎn):它需要給每個(gè)對(duì)象附加一個(gè)關(guān)聯(lián)引用計(jì)數(shù)灶体,并且引用計(jì)數(shù)無法解決循環(huán)引用的問題,因此JVM并沒有采用引用計(jì)數(shù)
2.可達(dá)性分析
跟蹤收集器的方法為停止應(yīng)用的工作汪厨,然后開始跟蹤對(duì)象赃春,跟蹤時(shí)從對(duì)象根開始沿著引用跟蹤愉择,直到檢查完所有的對(duì)象
根對(duì)象來源:常量池中的對(duì)象引用劫乱,本地方法中沒被“釋放”的對(duì)象引用织中,虛擬機(jī)運(yùn)行時(shí)數(shù)據(jù)區(qū)中從垃圾收集器的堆中分配的部分
缺點(diǎn):當(dāng)使用分代思想實(shí)現(xiàn)垃圾回收器的時(shí)候,minor GC需要掃描老年代是否引用年輕代的對(duì)象衷戈,計(jì)算量巨大
3.卡表(CardTable):針對(duì)跨代引用的掃描進(jìn)行優(yōu)化
卡片標(biāo)記的算法為將老年代以某個(gè)大邢梁稹(例如512字節(jié))進(jìn)行劃分,劃分出來的每個(gè)區(qū)域稱為卡片殖妇,JVM采用卡表維護(hù)卡的狀態(tài),每張卡片在卡表中占用一個(gè)字節(jié)的標(biāo)識(shí)(有些JVM實(shí)現(xiàn)可能會(huì)不同)疲吸,當(dāng)Java代碼執(zhí)行過程中發(fā)現(xiàn)老年代對(duì)象引用或釋放了對(duì)于新生代對(duì)象的引用時(shí)前鹅,就相應(yīng)的修改卡表中卡的狀態(tài)舰绘,每次Minor GC只需掃描卡表中標(biāo)識(shí)為臟狀態(tài)的卡中的對(duì)象即可
4.三色標(biāo)記法
上述都是一次停頓完成掃描捂寿,但是CMS,G1 垃圾回收器是分階段掃描蔓彩,包括初始掃描粪小,并行掃描探膊,重標(biāo)記待榔。其中G1 使用了三色標(biāo)記法
白:對(duì)象沒有被標(biāo)記到锐锣,標(biāo)記階段結(jié)束后,會(huì)被當(dāng)做垃圾回收掉雕憔。
灰:對(duì)象被標(biāo)記了斤彼,但是它的field還沒有被標(biāo)記或標(biāo)記完(引用的對(duì)象沒有標(biāo)記完)蘸泻。
黑:對(duì)象被標(biāo)記了嘲玫,且它的所有field也被標(biāo)記完了(引用對(duì)象標(biāo)記完了)。