JVM基礎(chǔ)知識(shí)

本文參考鏈接

運(yùn)行時(shí)數(shù)據(jù)區(qū)

  • 堆:存放java對象實(shí)例庙楚,GC回收的地方,也稱GC堆
  • 方法區(qū):堆的邏輯部分趴樱,存放靜態(tài)變量馒闷,常量酪捡,類信息
  • 虛擬機(jī)棧:存放運(yùn)行時(shí)的java方法,局部變量纳账,(棧幀形式)
  • 本地方法棧:類似虛擬機(jī)棧逛薇,服務(wù)的時(shí)native方法
  • 程序計(jì)數(shù)器:線程切換,異常處理疏虫,指示下一行執(zhí)行的字節(jié)碼序號

虛擬機(jī)棧:

棧的FILO符合java方法間的調(diào)用

棧幀:

每個(gè)方法在執(zhí)行的時(shí)候會(huì)創(chuàng)建一個(gè)棧幀永罚,棧幀是虛擬機(jī)棧的棧元素,是虛擬機(jī)用于進(jìn)行方法調(diào)用和方法執(zhí)行的數(shù)據(jù)結(jié)構(gòu)卧秘;

  • 操作數(shù)棧:字節(jié)碼指令的寫入和讀取呢袱,用于運(yùn)算和參數(shù)傳遞
  • 局部變量表:用于存放參數(shù)和方法內(nèi)部定義的局部變量
  • 動(dòng)態(tài)連接:沒看懂
  • 方法出口:方法返回的字節(jié)碼指令,返回到方法被調(diào)用的位置斯议;

當(dāng)一個(gè)方法被執(zhí)行時(shí)产捞,會(huì)創(chuàng)建一個(gè)棧幀(當(dāng)前棧幀),這個(gè)棧幀處于棧頂哼御,在編譯時(shí)期坯临,棧幀中需要的操作數(shù)棧和局部變量表的大小已經(jīng)完全確定,棧幀的內(nèi)存不會(huì)在運(yùn)行時(shí)受到影響恋昼;

  • 方法中的參數(shù)和局部變量會(huì)存放到局部變量表中看靠,當(dāng)這個(gè)方法是非static方法時(shí),局部變量表中的第一個(gè)數(shù)據(jù)是這個(gè)方法的實(shí)例對象液肌;如果局部變量是一個(gè)對象實(shí)例挟炬,就會(huì)在局部變量表中以reference引用出現(xiàn),上圖的obj就是一個(gè)reference引用嗦哆,它的真是指向是堆中的Object對象谤祖;
  • 操作數(shù)棧:先將num入棧,再將1入棧老速,取出棧頂?shù)膬蓚€(gè)元素粥喜,相加再入棧,此時(shí)棧中只剩下num+1了橘券;再出棧额湘,將num+1這個(gè)數(shù)賦值給局部變量表中的num;上面的操作就是num = num + 1的實(shí)現(xiàn)旁舰;
動(dòng)態(tài)連接:
  • 將虛方法的符號引用轉(zhuǎn)為直接引用:
    在類加載的解析階段玛追,會(huì)將運(yùn)行時(shí)常量池的符號引用轉(zhuǎn)換為直接引用,這個(gè)這個(gè)操作叫做靜態(tài)解析,但是它只會(huì)解析非虛方法(靜態(tài)方法恤筛,私有方法寿羞,構(gòu)造方法嘹狞,final修飾方法)的符號引用,而棧幀中的動(dòng)態(tài)連接就是在運(yùn)行時(shí)將其余的符號引用轉(zhuǎn)換為直接引用磺樱;
  • 分派(靜態(tài)分派纳猫,動(dòng)態(tài)分派):選擇多態(tài)的執(zhí)行版本
      Person person = new Man();

在上述代碼中紧阔,Person類是person對象的靜態(tài)類型,Man類是person對象的實(shí)際類型续担,靜態(tài)類型和實(shí)際類型都可以發(fā)生改變,通過強(qiáng)轉(zhuǎn)改變靜態(tài)類型,重新new一個(gè)對象改變實(shí)際類型活孩,所以靜態(tài)類型的確定是在編譯期物遇,實(shí)際類型的確定在代碼運(yùn)行時(shí);
重載是通過靜態(tài)類型來判斷執(zhí)行版本憾儒,這個(gè)稱為靜態(tài)分派
重寫是通過實(shí)際類型來判斷執(zhí)行版本询兴,這個(gè)稱為動(dòng)態(tài)分派

棧幀數(shù)據(jù)的通信:

方法和方法之間的數(shù)據(jù)交互是通過使用同一塊內(nèi)存完成的,即棧幀A的本地變量表可能是棧幀B的操作數(shù)棧起趾,同一塊內(nèi)存對于不同的棧幀是不同的角色诗舰;

虛擬機(jī)對于棧的優(yōu)化:方法內(nèi)聯(lián)

方法A調(diào)用方法B,將方法B的代碼作為方法A的一部分來執(zhí)行训裆,從而避免創(chuàng)建棧幀眶根,減少棧內(nèi)存的消耗

類加載過程

深入理解Java虛擬機(jī)插圖

上面的類加載過程中,加載边琉,驗(yàn)證属百,準(zhǔn)備,初始化变姨,卸載族扰,這五個(gè)步驟的順序是確定的,必須按照這個(gè)順序進(jìn)行定欧,解析階段在一些情況下可以在初始化之后進(jìn)行渔呵;

加載:

  • 通過類的全限定名獲取定義這個(gè)類的二進(jìn)制字節(jié)流
  • 將字節(jié)流代表的靜態(tài)存儲(chǔ)結(jié)構(gòu)轉(zhuǎn)化為方法區(qū)的運(yùn)行時(shí)數(shù)據(jù)結(jié)構(gòu)
  • 在內(nèi)存中生成一個(gè)這個(gè)類的class對象,作為方法區(qū)的訪問入口砍鸠;

獲取二進(jìn)制字節(jié)流這一步是開發(fā)者在類加載中可控性最強(qiáng)的階段(通過自定義classLoader)

驗(yàn)證(連接第一步):

目的:確保Class文件的字節(jié)流符合當(dāng)前虛擬機(jī)的要求扩氢,不會(huì)對虛擬機(jī)的安全產(chǎn)生威脅;class字節(jié)碼文件不一定通過.java源文件編譯來睦番,(動(dòng)態(tài)代理原理中是通過特定接口生成类茂,還可以通過網(wǎng)絡(luò)<Applet>,其他文件<JSP>等方式生成class字節(jié)碼)托嚣,所以巩检,通過其他途徑生成的字節(jié)碼文件,可以做java語言做不到的事情示启,這些可能導(dǎo)致系統(tǒng)的奔潰兢哭;

驗(yàn)證內(nèi)容:
  • 文件格式驗(yàn)證:字節(jié)流是否符合Class文件格式規(guī)范
  • 元數(shù)據(jù)驗(yàn)證:字節(jié)碼內(nèi)容的元數(shù)據(jù)信息是否符合java語言規(guī)范
  • 字節(jié)碼驗(yàn)證:校驗(yàn)類的方法體在運(yùn)行是會(huì)不會(huì)危害虛擬機(jī)
  • 符號引用驗(yàn)證:對類自身以外的信息校驗(yàn)

準(zhǔn)備(連接第二步):

為類的靜態(tài)變量分配內(nèi)存(方法區(qū)),并且初始化靜態(tài)變量為0/false/null夫嗓;

注意:這里是將static變量分配到方法區(qū)迟螺,而不是實(shí)例變量冲秽,實(shí)例變量是在對象創(chuàng)建的時(shí)候分配在中的;

解析(連接第三步):

將常量池中的符號引用轉(zhuǎn)為直接引用

  • 符號引用:一組描述引用目標(biāo)的符號矩父,存放在Class文件的常量池中锉桑,符號引用和虛擬機(jī)實(shí)現(xiàn)的內(nèi)存布局無關(guān),如果不經(jīng)過轉(zhuǎn)換無法得到真正的內(nèi)存地址窍株;

  • 直接引用:能直接指向目標(biāo)的指針或間接定位到目標(biāo)的句柄民轴,直接引用和虛擬機(jī)實(shí)現(xiàn)的內(nèi)存布局相關(guān),能直接找到內(nèi)存中的目標(biāo)球订;
    https://cloud.tencent.com/developer/article/1450501

常量池種類:

  1. class文件常量池:編譯后后裸,class常量池存放 字面量符號引用
  2. 全局字符串常量池:類加載階段,在堆中創(chuàng)建字符串對象冒滩,對象的真實(shí)引用放入全局常量池微驶,1.7以后,全局字符串常量池被移動(dòng)到了堆內(nèi)开睡;
  3. 運(yùn)行時(shí)常量池:類加載階段(二進(jìn)制字節(jié)碼代表的靜態(tài)數(shù)據(jù)結(jié)構(gòu)轉(zhuǎn)為方法區(qū)的運(yùn)行時(shí)數(shù)據(jù)結(jié)構(gòu))因苹,將class常量池的符號引用放入運(yùn)行時(shí)常量池, 解析階段,將運(yùn)行時(shí)常量池的符號引用轉(zhuǎn)為和全局常量池一致的直接引用
  4. 基本類型包裝類對象常量池:

初始化:

執(zhí)行類構(gòu)造器clinit(),初始化類變量和其他資源士八,在準(zhǔn)備階段容燕,我們?yōu)轭愖兞吭诜椒▍^(qū)分配了內(nèi)存,并且將類變量置為0婚度,false蘸秘,/null ,在初始化階段會(huì)將類變量設(shè)置為代碼中的實(shí)際值蝗茁,如果類中有靜態(tài)代碼塊也會(huì)在這一階段執(zhí)行醋虏;

注意:
  • clinit()是由編譯器自動(dòng)收集的所有類變量賦值行為和靜態(tài)代碼塊中的語句合并產(chǎn)生;
  • 虛擬機(jī)保證子類的clinit()執(zhí)行前哮翘,一定會(huì)先執(zhí)行父類的clinit()颈嚼,所以第一個(gè)執(zhí)行的clinit()一定是Object類的;
  • 如果一個(gè)類沒有賦值操作和靜態(tài)代碼塊饭寺,那就沒有必要執(zhí)行clinit()阻课,虛擬機(jī)就不會(huì)執(zhí)行
  • 接口執(zhí)行clinit()前不需要執(zhí)行父類的clinit(),接口的實(shí)現(xiàn)類在初始化時(shí)也不會(huì)執(zhí)行接口的clinit()
  • 虛擬機(jī)保證clinit()執(zhí)行時(shí)的線程安全(加鎖艰匙,其他線程阻塞限煞,釋放鎖)

對象創(chuàng)建過程

  • 設(shè)置對象:對對象進(jìn)行必要的設(shè)置,設(shè)置HashCode员凝,GC分代年齡等
  • init方法:初始化實(shí)例變量的值(代碼中設(shè)置的值)

對象內(nèi)存布局

  • 對其填充其實(shí)是一個(gè)占位符沒有特別的意義署驻,對象大小必須是8字節(jié)的整數(shù)倍,通過對其填充來確保是8的整數(shù)倍;

對象訪問

句柄訪問:
直接訪問:

可達(dá)性分析法——判斷對象是否需要回收

可達(dá)性分析法:

  • 可達(dá)性分析:如果一個(gè)對象到GCRoot對象沒有引用鏈旺上,則表明不可達(dá)瓶蚂;
GCRoot對象:
  • Java虛擬機(jī)棧中的引用對象
  • 本地方法棧中JNI(Native)引用的對象
  • 方法區(qū)中引用的常量靜態(tài)屬性對象
public class OOM {
    
    private int rootConstant = 1000; // 方法區(qū)常量對象GCRoot
    private static Object rootStatic = new Object(); // 方法區(qū)靜態(tài)對象GCRoot
    private Object nonRoot = new Object(); // 類實(shí)例對象宣吱,非GCRoot

    public void test (){
        Object rootJavaStack = new Object();  //  虛擬機(jī)棧引用的對象GCRoot

        // 可達(dá)窃这,不會(huì)回收
        Object obj0 = rootConstant;
        Object obj1 = rootStatic;
        Object obj2 = rootJavaStack;
        // 不可達(dá),回收
        Object obj3 = nonRoot;
    }
    
}
Java引用類型:
  • 強(qiáng)引用 StrongReference:普通引用
  • 軟引用 SoftReference:內(nèi)存不足時(shí)會(huì)回收軟引用對象(展示圖片使用內(nèi)存)
  • 弱引用 WeakReference:放生GC時(shí)會(huì)回收弱引用對象(ThreadLocal的Entry使用內(nèi)存)
  • 虛引用 PhantomReference:最弱的引用(日常開發(fā)用不到)
生存還是死亡:

其實(shí)在被認(rèn)定了不可達(dá)以后征候,還需要經(jīng)過兩次標(biāo)記篩選钦听;

  • 一次標(biāo)記:如果一個(gè)對象到GCRoot沒有引用,則需要進(jìn)行一次標(biāo)記:是否有必要執(zhí)行finalize()倍奢,當(dāng)對象沒有覆蓋finalize()或者finalize()已經(jīng)被虛擬機(jī)執(zhí)行過,則表明沒有必要執(zhí)行垒棋;
  • 二次標(biāo)記:如果對象有必要執(zhí)行finalize()卒煞,則進(jìn)行二次標(biāo)記:將對象放入一個(gè)F-Queue隊(duì)列,虛擬機(jī)創(chuàng)建一個(gè)優(yōu)先級特別低的線程Finalizer執(zhí)行隊(duì)列的對象的finalize方法叼架,在執(zhí)行前會(huì)再次判斷這個(gè)對象是否有到GCroot的引用鏈畔裕,如果還沒有,則真正回收乖订;

垃圾回收算法

復(fù)制算法:

復(fù)制算法
特點(diǎn):
  • 只能使用一半內(nèi)存
  • 沒有內(nèi)存碎片
  • 需要復(fù)制內(nèi)存

標(biāo)記清除:

標(biāo)記清除算法
特點(diǎn):
  • 效率低
  • 會(huì)出現(xiàn)內(nèi)存碎片

標(biāo)記整理:

標(biāo)記整理算法
特點(diǎn):
  • 步驟較多
  • 不會(huì)出現(xiàn)內(nèi)存碎片

分代收集算法:

將內(nèi)存分為新時(shí)代和老年代扮饶,新生代再分為Eden和Survivor(from to)區(qū)


  • 對象優(yōu)先在Eden區(qū)分配
  • 大對象直接進(jìn)入老年代
  • 長期存活的對象進(jìn)入老年代:在Eden區(qū)中經(jīng)過一次Minor GC仍然存活就轉(zhuǎn)移到Survivor區(qū),在對象頭中存放的GC年齡+1乍构,每經(jīng)過一次Minor GC甜无,F(xiàn)rom和To就會(huì)發(fā)生一次復(fù)制,對象的年齡就會(huì)+1哥遮,默認(rèn)情況下岂丘,一個(gè)對象經(jīng)過15此Minor GC仍然存活就移動(dòng)到老年代;
  • 動(dòng)態(tài)對象年齡判定:如果在Survivor區(qū)中的同一年齡的對象的內(nèi)存和大小大于Survivor(From/To)內(nèi)存的一半眠饮,就直接移動(dòng)到老年代奥帘;

GC發(fā)生的條件:空間不夠(Eden/老年代滿了,繼續(xù)放對象時(shí)仪召,會(huì)觸發(fā)GC)

收集行為 算法 特點(diǎn) 發(fā)生時(shí)刻
新生代 Minor GC 復(fù)制算法 回收頻率高寨蹋,對象存活率低 Eden區(qū)無可分配內(nèi)存
老年代 Full GC 標(biāo)記整理/ 標(biāo)記清除 回收頻率低,對象存活率高 老年代無可分配內(nèi)存

垃圾收集器()
吞吐量 = 業(yè)務(wù)線程運(yùn)行時(shí)間/總CPU時(shí)間

常見的七種垃圾收集器
垃圾收集器 收集器類型 收集對象 算法
Serial 單線程 新生代 復(fù)制
ParNew 并行的多線程 新生代 復(fù)制
Parallel Scavenge 并行的多線程 新生代 復(fù)制
Serial Old 單線程 老年代 標(biāo)記整理
Parallel Old 并行的多線程 老年代 標(biāo)記整理
CMS 并行并發(fā)的收集器 老年代 標(biāo)記清除
G1 并發(fā)并行的收集器 新生代&老年代 復(fù)制+標(biāo)記整理
單線程垃圾收集器工作示意圖
多線程垃圾收集器工作示意圖
CMS垃圾收集器:Concurrent Mark Sweep
步驟:
  • 初始標(biāo)記Stop the world
  • 并發(fā)標(biāo)記
  • 重新標(biāo)記Stop the world
  • 并發(fā)清除
CMS收集器工作示意圖
特點(diǎn):
  • 最短停頓回收時(shí)間:耗時(shí)最長并發(fā)標(biāo)記和并發(fā)清除可以同用戶線程一起運(yùn)行扔茅,總體上可以說CMS收集器是同用戶線程一起并發(fā)執(zhí)行的
  • 降低總吞吐量:并發(fā)標(biāo)記和并發(fā)清除會(huì)占用CPU資源已旧,降低吞吐量
  • 無法清除浮動(dòng)垃圾:在并發(fā)清除時(shí)用戶線程產(chǎn)生的垃圾稱為 "浮動(dòng)垃圾"
  • 堆內(nèi)存不規(guī)整:標(biāo)記清除會(huì)導(dǎo)致堆內(nèi)存不規(guī)整;
G1收集器:Garbage First

最新咖摹、技術(shù)最前沿的垃圾收集器

步驟:
  • 初始標(biāo)記
  • 并發(fā)標(biāo)記
  • 最終標(biāo)記
  • 篩選回收
G1收集器工作示意圖
特點(diǎn):
  • 不降低吞吐量的情況下減少停頓時(shí)間:可預(yù)測停頓

  • 分代收集:可以回收新生代和老年代

  • 不會(huì)產(chǎn)生內(nèi)存碎片

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末评姨,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌吐句,老刑警劉巖胁后,帶你破解...
    沈念sama閱讀 222,104評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異嗦枢,居然都是意外死亡攀芯,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,816評論 3 399
  • 文/潘曉璐 我一進(jìn)店門文虏,熙熙樓的掌柜王于貴愁眉苦臉地迎上來侣诺,“玉大人,你說我怎么就攤上這事氧秘∧暝В” “怎么了?”我有些...
    開封第一講書人閱讀 168,697評論 0 360
  • 文/不壞的土叔 我叫張陵丸相,是天一觀的道長搔确。 經(jīng)常有香客問我,道長灭忠,這世上最難降的妖魔是什么膳算? 我笑而不...
    開封第一講書人閱讀 59,836評論 1 298
  • 正文 為了忘掉前任,我火速辦了婚禮弛作,結(jié)果婚禮上涕蜂,老公的妹妹穿的比我還像新娘。我一直安慰自己映琳,他們只是感情好机隙,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,851評論 6 397
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著萨西,像睡著了一般黍瞧。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上原杂,一...
    開封第一講書人閱讀 52,441評論 1 310
  • 那天印颤,我揣著相機(jī)與錄音,去河邊找鬼穿肄。 笑死年局,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的咸产。 我是一名探鬼主播矢否,決...
    沈念sama閱讀 40,992評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼脑溢!你這毒婦竟也來了僵朗?” 一聲冷哼從身側(cè)響起赖欣,我...
    開封第一講書人閱讀 39,899評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎验庙,沒想到半個(gè)月后顶吮,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,457評論 1 318
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡粪薛,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,529評論 3 341
  • 正文 我和宋清朗相戀三年悴了,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片违寿。...
    茶點(diǎn)故事閱讀 40,664評論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡湃交,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出藤巢,到底是詐尸還是另有隱情搞莺,我是刑警寧澤,帶...
    沈念sama閱讀 36,346評論 5 350
  • 正文 年R本政府宣布掂咒,位于F島的核電站腮敌,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏俏扩。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,025評論 3 334
  • 文/蒙蒙 一弊添、第九天 我趴在偏房一處隱蔽的房頂上張望录淡。 院中可真熱鬧,春花似錦油坝、人聲如沸嫉戚。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,511評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽彬檀。三九已至,卻和暖如春瞬女,著一層夾襖步出監(jiān)牢的瞬間窍帝,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,611評論 1 272
  • 我被黑心中介騙來泰國打工诽偷, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留坤学,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 49,081評論 3 377
  • 正文 我出身青樓报慕,卻偏偏與公主長得像深浮,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子眠冈,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,675評論 2 359