JVM垃圾回收策略

java內(nèi)存區(qū)域劃分

Java與C++之間有一堵由內(nèi)存動(dòng)態(tài)分配和垃圾收集技術(shù)所圍成的“高墻”,墻外面的人想進(jìn)去,墻里面的人卻想出來(lái)。



java虛擬機(jī)在執(zhí)行java程序的過(guò)程中,會(huì)把它管理的內(nèi)存劃分為若干個(gè)不同的數(shù)據(jù)區(qū)域,如下圖所示
圖片.png

  • 程序計(jì)數(shù)器

程序計(jì)數(shù)器是一塊較小的內(nèi)存空間,可以看作是當(dāng)前線程所執(zhí)行的字節(jié)碼的信號(hào)指示器
每條線程都需要一個(gè)獨(dú)立的程序指示器,是為了程序切換后能恢復(fù)到正確的位置。
此內(nèi)存區(qū)域是唯一一個(gè)在java虛擬機(jī)規(guī)范中沒(méi)有規(guī)定任何OutOfMemoryError的區(qū)域

  • java虛擬機(jī)棧

    Java虛擬機(jī)棧是線程私有的,它的生命周期與線程相同。虛擬機(jī)棧描述的是Java方法執(zhí)行的內(nèi)存模型:每個(gè)方法執(zhí)行的同時(shí)會(huì)創(chuàng)建一個(gè)棧幀,棧幀用于存儲(chǔ)局部變量表、操作數(shù)棧、動(dòng)態(tài)鏈接、方法出口等信息。每個(gè)方法從調(diào)用直至執(zhí)行完成的過(guò)程,就對(duì)應(yīng)著一個(gè)棧幀在虛擬機(jī)棧中入棧到出棧的過(guò)程。
    局部變量表存放了編譯期間可知的各種基本數(shù)據(jù)類型,對(duì)象引用和returnAddress類型
    局部變量表所需的內(nèi)存空間在編譯期間完成分配,在方法運(yùn)行期間不會(huì)改變局部變量表的大小

  • 本地方法棧
    本地方法棧為虛擬機(jī)使用的Nativie方法服務(wù)

  • java堆
    java堆是被所有線程共享的內(nèi)存區(qū)域,在虛擬機(jī)啟動(dòng)的時(shí)候創(chuàng)建,此內(nèi)存區(qū)域的唯一目的就是存放對(duì)象的實(shí)例
    java堆是垃圾收集器管理的主要區(qū)域

  • 方法區(qū)
    方法區(qū)也是被所有線程共享的內(nèi)存區(qū)域,用于存儲(chǔ)已被虛擬機(jī)加載的類信息、常量、靜態(tài)變量、即時(shí)編譯器編譯后的代碼等數(shù)據(jù)
    這區(qū)域的內(nèi)存回收目標(biāo)主要是針對(duì)常量池的回收和類型的卸載,一般而言,這個(gè)區(qū)域的內(nèi)存回收比較難以令人滿意,尤其是類型的回收,條件相當(dāng)苛刻,但是這部分區(qū)域的內(nèi)存回收確實(shí)是必要的。

  • 運(yùn)行時(shí)常量池
    運(yùn)行時(shí)常量池是方法區(qū)的一部分,用于存放編譯期生成的各種字面量和符號(hào)引用

程序計(jì)數(shù)器、虛擬機(jī)棧、本地方法棧3個(gè)區(qū)域隨線程而生、隨線程而滅
棧中的棧幀隨著方法的進(jìn)入和退出而有條不紊地執(zhí)行著入棧和出棧操作。每一個(gè)棧楨中分配多少內(nèi)存是在類結(jié)構(gòu)已經(jīng)確定下來(lái)就已知的,這幾個(gè)區(qū)域的內(nèi)存分配和回收多具備確定性,這幾個(gè)區(qū)域不需要過(guò)多考慮回收的問(wèn)題,方法結(jié)束或者線程結(jié)束時(shí)。內(nèi)存就跟著自動(dòng)回收了。

java堆和方法區(qū),只有在程序運(yùn)行期間才知道會(huì)創(chuàng)建哪些對(duì)象,內(nèi)存的分配和回收都是動(dòng)態(tài)的。

JVM在進(jìn)行垃圾回收前,需要判斷哪些對(duì)象是需要回收的

  • 引用計(jì)數(shù)算法

給對(duì)象添加一個(gè)引用計(jì)數(shù)器,被引用時(shí)計(jì)數(shù)器值+1,引用失效計(jì)數(shù)器值-1,當(dāng)計(jì)數(shù)器值為0時(shí)對(duì)象不可能再被使用;

主流Java虛擬機(jī)未選用該算法管理內(nèi)存(未解決對(duì)象之間相互循環(huán)引用的問(wèn)題)

實(shí)現(xiàn)簡(jiǎn)單,判斷效率高(應(yīng)用:FlashPlayer,Python等)

  • 可達(dá)性分析算法

將"GC Roots"對(duì)象作為起始節(jié)點(diǎn),向下搜索,搜索走過(guò)的路徑為引用鏈;當(dāng)一個(gè)對(duì)象到GC Roots沒(méi)有引用鏈時(shí),則該對(duì)象是不可用的;

可作為"GC Roots"的對(duì)象:

1.方法區(qū)中靜態(tài)屬性引用的對(duì)象

2.方法區(qū)中常量引用的對(duì)象

3.虛擬機(jī)棧引用的對(duì)象 (棧幀中本地變量表)

4.本地方法棧中JNI引用的對(duì)象 (Native方法)

GC算法

  • 標(biāo)記 - 清除算法

<figure>
image

</figure>

標(biāo)記-清除算法采用從根集合進(jìn)行掃描,對(duì)存活的對(duì)象進(jìn)行標(biāo)記,標(biāo)記完畢后,再掃描整個(gè)空間中未被標(biāo)記的對(duì)象進(jìn)行直接回收,如上圖。

標(biāo)記-清除算法不需要進(jìn)行對(duì)象的移動(dòng),并且僅對(duì)不存活的對(duì)象進(jìn)行處理,在存活的對(duì)象比較多的情況下極為高效,但由于標(biāo)記-清除算法直接回收不存活的對(duì)象,并沒(méi)有對(duì)還存活的對(duì)象進(jìn)行整理,因此會(huì)導(dǎo)致內(nèi)存碎片。

  • 復(fù)制算法
image

復(fù)制算法將內(nèi)存劃分為兩個(gè)區(qū)間,使用此算法時(shí),所有動(dòng)態(tài)分配的對(duì)象都只能分配在其中一個(gè)區(qū)間(活動(dòng)區(qū)間),而另外一個(gè)區(qū)間(空間區(qū)間)則是空閑的。

復(fù)制算法采用從根集合掃描,將存活的對(duì)象復(fù)制到空閑區(qū)間,當(dāng)掃描完畢活動(dòng)區(qū)間后,會(huì)的將活動(dòng)區(qū)間一次性全部回收。此時(shí)原本的空閑區(qū)間變成了活動(dòng)區(qū)間。下次GC時(shí)候又會(huì)重復(fù)剛才的操作,以此循環(huán)。

復(fù)制算法在存活對(duì)象比較少的時(shí)候,極為高效,但是帶來(lái)的成本是犧牲一半的內(nèi)存空間用于進(jìn)行對(duì)象的移動(dòng)。所以復(fù)制算法的使用場(chǎng)景,必須是對(duì)象的存活率非常低才行,而且最重要的是,我們需要克服50%內(nèi)存的浪費(fèi)。

  • 標(biāo)記 - 整理算法
image

標(biāo)記-整理算法采用 標(biāo)記-清除 算法一樣的方式進(jìn)行對(duì)象的標(biāo)記、清除,但在回收不存活的對(duì)象占用的空間后,會(huì)將所有存活的對(duì)象往左端空閑空間移動(dòng),并更新對(duì)應(yīng)的指針。標(biāo)記-整理 算法是在標(biāo)記-清除 算法之上,又進(jìn)行了對(duì)象的移動(dòng)排序整理,因此成本更高,但卻解決了內(nèi)存碎片的問(wèn)題。

JVM為了優(yōu)化內(nèi)存的回收,使用了分代回收的方式,對(duì)于新生代內(nèi)存的回收(Minor GC)主要采用復(fù)制算法。而對(duì)于老年代的回收(Major GC),大多采用標(biāo)記-整理算法

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容