摘要:原文出處垃圾回收機(jī)制標(biāo)記清除算法介紹最主要的理論算法之一,在實(shí)踐過(guò)程中,為了真實(shí)情景需要,需要許多調(diào)整。因此不會(huì)僅僅標(biāo)記清除,垃圾回收期間,內(nèi)存整理進(jìn)程同時(shí)在工作。不同內(nèi)存區(qū)域的垃圾收集機(jī)制不辣么容易理解。
原文出處:java垃圾回收機(jī)制
標(biāo)記清除算法介紹最主要的理論算法之一,在實(shí)踐過(guò)程中,為了真實(shí)情景需要,需要許多調(diào)整。舉一個(gè)簡(jiǎn)單例子,我們檢查JVM需要做的各種事情,以便我們安全地去創(chuàng)建對(duì)象。
清除壓縮
當(dāng)清除期間,JVM必須確保區(qū)域被不可達(dá)對(duì)象填充。這會(huì)(終將會(huì))導(dǎo)致內(nèi)存碎片化,同樣會(huì)導(dǎo)致磁盤碎片化,由此產(chǎn)生兩個(gè)問(wèn)題:
寫操作因?yàn)閷ふ蚁乱粋€(gè)足夠尺寸的空間變得耗費(fèi)時(shí)間,這個(gè)寫操作不再簡(jiǎn)單。
當(dāng)創(chuàng)建新對(duì)象的時(shí)候,JVM分配一個(gè)連續(xù)的空間。如果內(nèi)存碎片遍布每一個(gè)點(diǎn),沒(méi)有足夠的空間容納新創(chuàng)建的對(duì)象,分配就會(huì)發(fā)生錯(cuò)誤。
為了避免上面的問(wèn)題,JVM會(huì)確保碎片化不會(huì)失控。因此不會(huì)僅僅標(biāo)記清除,垃圾回收期間,"內(nèi)存整理"進(jìn)程同時(shí)在工作。這個(gè)進(jìn)程重新分配所有的可達(dá)對(duì)象讓他們緊密排列,消除(或者減少)碎片。下面是示意圖:
分代假設(shè)
正如我們之前提到,垃圾收集會(huì)引起應(yīng)用徹底停頓。對(duì)象越多回收垃圾花費(fèi)的時(shí)間越長(zhǎng),這是顯而易見(jiàn)的。如果我們把使用一小塊內(nèi)存工作變成可能,那又會(huì)怎樣呢?為了研究這種可行性,一些專家發(fā)現(xiàn)應(yīng)用的大多數(shù)內(nèi)存分為以下兩種情況:
絕大多數(shù)對(duì)象很快就成為無(wú)用對(duì)象。
很少部分對(duì)象講過(guò)很長(zhǎng)時(shí)間存活下來(lái)。
上面的觀察結(jié)果歸類于新生代假設(shè)?;谶@個(gè)假設(shè):VM內(nèi)存空間被劃分為Young代 和Old代,后者有時(shí)也叫做Tenured。
眾多算法在提升GC性能上已經(jīng)取得進(jìn)展,使得擁有這樣一個(gè)獨(dú)立易清除的內(nèi)存區(qū)域變成現(xiàn)實(shí)。
這種方式雖說(shuō)不上毫無(wú)問(wèn)題。當(dāng)垃圾收集器收集一個(gè)分代中的對(duì)象的時(shí)候,不同分代中的對(duì)象彼此相互引用的時(shí),實(shí)際上被當(dāng)作"GC roots"。
但是更更要的一點(diǎn)是,分代假設(shè)并不適用于一些應(yīng)用。自此,因?yàn)槟切柏舱邸焙汀坝锌赡苡郎钡膶?duì)象,GC算法做了優(yōu)化,JVM對(duì)那些期待更久生命的對(duì)象表現(xiàn)得友好。
內(nèi)存空間
讀者應(yīng)該了解下圖中java堆區(qū)里面的內(nèi)存劃分。不同內(nèi)存區(qū)域的垃圾收集機(jī)制不辣么容易理解。應(yīng)該注意,不同GC算法實(shí)現(xiàn)細(xì)節(jié)可能不同,然而它們的理念是一致的。
Eden
Eden區(qū)是對(duì)象被新創(chuàng)建的時(shí)候分配的內(nèi)存區(qū)域。在這個(gè)區(qū)域中,多線程可以同時(shí)創(chuàng)建多個(gè)對(duì)象。在Eden區(qū)里,Eden 被分成一個(gè)或多個(gè)Thread Local Allocation Buffer (縮寫:TLAB)。在這些緩存里,JVM允許線程在對(duì)應(yīng)的緩存中分配絕大多數(shù)的對(duì)象,避免昂貴的多線程同步。
當(dāng)TLAB中不能分配空間時(shí)(因?yàn)榭臻g不足),JVM會(huì)移到共享的Eden區(qū)去分配,如果共享Eden空間也不足時(shí),Young代中垃圾回收器去釋放更多的空間。如果在GC之后還沒(méi)有足夠的空間以供使用,對(duì)象會(huì)在Old代中分配。
當(dāng)Eden回收期間,GC把所有的可達(dá)對(duì)象標(biāo)記為存活。
我們已經(jīng)提前注意到,對(duì)象可以跨代關(guān)聯(lián),因此我們必須有一個(gè)快捷途徑去檢查其他代的對(duì)象引用Eden區(qū)中的對(duì)象。
從一開(kāi)始就記錄所有的分代引用是不可取的, JVM有自己的機(jī)制:卡標(biāo)記。事實(shí)上,JVM僅僅標(biāo)記Eden里面有可能Old代引用Eden代的對(duì)象的“臟”對(duì)象的位置,你可以在Nitsan的博客里了解更多的信息。
標(biāo)記階段完成之后,Eden區(qū)下面所有存活的對(duì)象被復(fù)制到Survivor下面的一塊區(qū)域中?,F(xiàn)在,Eden區(qū)被清空,重新分配新建對(duì)象。正如“標(biāo)記-復(fù)制”的名稱一樣:存活的對(duì)象被標(biāo)記,然后復(fù)制(不是移動(dòng))到Survivor區(qū)。
Survivor區(qū)
緊鄰Eden區(qū)的下一個(gè)區(qū)域時(shí)兩個(gè)叫做from和to的Survivor區(qū)。需要注意的一點(diǎn),兩塊區(qū)域中的一塊是空的。
Survivor中的空的區(qū)域會(huì)保存下一刻Young代中垃圾回收后的對(duì)象。Young代所有存活的對(duì)象(包括Eden區(qū)和Survior區(qū)中非空的from區(qū)域)被復(fù)制到Survivor區(qū)的"to"區(qū)域。在這之后,“to”區(qū)域存放所有對(duì)象,“from”區(qū)域清空。兩者進(jìn)行調(diào)換(譯者注:即from變成to,to變成from)。
在兩個(gè)區(qū)域進(jìn)行數(shù)次復(fù)制存活對(duì)象操作,直到一些對(duì)象足夠成熟(“old enough”)。記住這一點(diǎn),基于分代假設(shè),一些對(duì)象在數(shù)次GC之后存活下來(lái),而且在很長(zhǎng)一段時(shí)間內(nèi)繼續(xù)被引用。
這樣的“tenured”對(duì)象會(huì)升級(jí)到Old代。這種情況發(fā)生的時(shí)候,對(duì)象不再?gòu)腟urvuvor區(qū)一個(gè)區(qū)域移動(dòng)到另外一個(gè)區(qū)域,而是進(jìn)入Old區(qū),在成為不可達(dá)對(duì)象之前它們一直存在在old區(qū)中。
為了確定哪些對(duì)象“old enough”,需要為old區(qū)提供一種算法,GC記錄幸存對(duì)象的詳細(xì)信息。每代GC完成之后,那些依然存活的對(duì)象年齡進(jìn)行增長(zhǎng)。每當(dāng)年齡超過(guò)設(shè)定的閥值之后,對(duì)象才會(huì)被升遷到old區(qū)。
實(shí)際上閥值被JVM動(dòng)態(tài)設(shè)定,-XX:+MaxTenuringThreshold 除外,它設(shè)置最高限定。XX:+MaxTenuringThreshold=0 表示跳過(guò)Survivor區(qū)的兩個(gè)區(qū)域之間復(fù)制過(guò)程直接進(jìn)入old區(qū)。jvm默認(rèn)閥值是GC循環(huán)15次。在Hotspot是最大值。
Survivor區(qū)空間不足以容納Young代所有存活對(duì)象的時(shí)候升遷操作被提前觸發(fā)。
Old代
Old代的具體實(shí)現(xiàn)細(xì)節(jié)巨復(fù)雜。Old代通常被那些幾乎不可能被當(dāng)作垃圾的對(duì)象占據(jù)。
Old代GC觸發(fā)的次數(shù)比Young代少。因此,Old代中的存活對(duì)象,不會(huì)發(fā)生標(biāo)記復(fù)制過(guò)程。相反,這些對(duì)象保持最小碎片化。這種算法建立在不同維度之上。大體上,分為以下幾步:
通過(guò)設(shè)置所有GC roots可達(dá)的對(duì)象標(biāo)記位來(lái)標(biāo)記所有可達(dá)對(duì)象。
刪除所有的不可達(dá)對(duì)象。
通過(guò)復(fù)制對(duì)象并緊密排列在Old區(qū)的頂端壓縮Old區(qū)的空間。
正如你看到上面描述的那樣,Old代的GC必須明確處理壓縮錯(cuò)左避免過(guò)多的碎片。
PermGen
JAVA 8之前中被稱作“Permanent Generation”的特殊區(qū)域。這是以前存放例如class的metadata。而,Permgen還存儲(chǔ)String之類的額外數(shù)據(jù)。實(shí)際上為JAVA開(kāi)發(fā)者添加了許多麻煩,因?yàn)楹茈y預(yù)測(cè)到底需要多少的空間。這些錯(cuò)誤的預(yù)測(cè)結(jié)果表現(xiàn)形式為java.lang.OutOfMemoryError: Permgen space。除非是類似OutOfMemoryError的原因是真的是因?yàn)閮?nèi)存泄漏,解決這種問(wèn)題的簡(jiǎn)單方法是增加permgen尺寸。下圖中設(shè)置permgen尺寸的最大值為256M:
java -XX:MaxPermSize=256m
Metaspace
正如預(yù)測(cè)metadata是一件紛繁復(fù)雜的事情那樣,JAVA 8移除了Permanent區(qū),換作Metaspace。從那時(shí)起,絕大多數(shù)復(fù)雜的事情都被移到Java heap區(qū)。
類定義文件,現(xiàn)在都存入叫做“Metaspace”的區(qū)域中。他相當(dāng)于本地內(nèi)存的一塊區(qū)域。理論上,Metaspace尺寸僅僅受限于JAVA進(jìn)程可獲得本地內(nèi)存大小。將JAVA開(kāi)發(fā)人員從僅僅在應(yīng)用多增加一個(gè)類就造成java.lang.OutOfMemoryError: Permgen space的困境中解脫出來(lái)。需要注意的是這個(gè)看起來(lái)不受限制沒(méi)有損失的空間-讓Metaspace無(wú)限制的增長(zhǎng)你會(huì)引起內(nèi)存重交換或者/和本地內(nèi)存分配失敗。
某些場(chǎng)合你希望保護(hù)自己,你可以如下圖所示限制Metaspace增長(zhǎng),Metaspace尺寸限制在265M:
java -XX:MaxMetaspaceSize=256m
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/66337.html
摘要:正好最近在學(xué)習(xí)的各種實(shí)現(xiàn)原理,在這里斗膽翻譯一篇垃圾回收機(jī)制原文鏈接。自動(dòng)管理的機(jī)制中,通常都會(huì)包含垃圾回收機(jī)制。二垃圾回收機(jī)制的概念垃圾回收,是一種自動(dòng)管理應(yīng)用程序所占內(nèi)存的機(jī)制,簡(jiǎn)稱方便起見(jiàn),本文均采用此簡(jiǎn)寫。 最近關(guān)注了一個(gè)國(guó)外技術(shù)博客RisingStack里面有很多高質(zhì)量,且對(duì)新手也很friendly的文章。正好最近在學(xué)習(xí)Node.js的各種實(shí)現(xiàn)原理,在這里斗膽翻譯一篇Node...
摘要:垃圾回收器追蹤所有正在使用的對(duì)象,將無(wú)用對(duì)象標(biāo)記為垃圾。自動(dòng)化指針內(nèi)存回收自動(dòng)化的最好方式之一是使用鉤子函數(shù)。它們可能因?yàn)槎喾N原因發(fā)生,但是這種垃圾回收器是最主流的一種。 原文出處:What Is Garbage Collection? 一眼就應(yīng)該從名稱看出垃圾回收機(jī)制的含義-查找垃圾,然后丟棄。事實(shí)正好相反。垃圾回收器追蹤所有正在使用的對(duì)象,將無(wú)用對(duì)象標(biāo)記為垃圾。請(qǐng)留意,我們開(kāi)始研究...
摘要:原文出處設(shè)計(jì)的一個(gè)重要目標(biāo)是設(shè)置階段的持續(xù)時(shí)長(zhǎng)和頻率,因?yàn)槔占骺深A(yù)測(cè),可配置。收集器盡自己最大努力高概率實(shí)現(xiàn)目標(biāo)但不是必然,它會(huì)是硬實(shí)時(shí)。因此名稱是收集器。運(yùn)行不同使用獨(dú)立的收集器。 原文出處:G1 – Garbage First G1設(shè)計(jì)的一個(gè)重要目標(biāo)是設(shè)置stop-the-world階段的持續(xù)時(shí)長(zhǎng)和頻率,因?yàn)槔占骺深A(yù)測(cè),可配置。事實(shí)上,G1是一款軟實(shí)時(shí)的收集器,意味著你...
摘要:原文出處這種垃圾收集器的官方名稱是。使用收集器的名稱。事件時(shí)長(zhǎng)記錄不同的類型回收期間垃圾收集器線程消耗事件調(diào)用操作系統(tǒng)活著等待系統(tǒng)事件消耗時(shí)間應(yīng)用停頓的時(shí)鐘時(shí)間?,F(xiàn)在我們看一些一些任務(wù)的時(shí)間,垃圾收集器線程等待很長(zhǎng)時(shí)間。 原文出處:Concurrent Mark and Sweep 這種垃圾收集器的官方名稱是Mostly Concurrent Mark and Sweep Garbag...
摘要:本文將會(huì)深入分析的引擎的內(nèi)部實(shí)現(xiàn)。該引擎使用在谷歌瀏覽器內(nèi)部。同其他現(xiàn)代引擎如或所做的一樣,通過(guò)實(shí)現(xiàn)即時(shí)編譯器在執(zhí)行時(shí)將代碼編譯成機(jī)器代碼。這可使正常執(zhí)行期間只發(fā)生相當(dāng)短的暫停。 原文 How JavaScript works: inside the V8 engine + 5 tips on how to write optimized code 幾周前我們開(kāi)始了一個(gè)系列博文旨在深入...
閱讀 1253·2021-11-24 09:39
閱讀 391·2019-08-30 14:12
閱讀 2605·2019-08-30 13:10
閱讀 2449·2019-08-30 12:44
閱讀 974·2019-08-29 16:31
閱讀 860·2019-08-29 13:10
閱讀 2455·2019-08-27 10:57
閱讀 3169·2019-08-26 13:57