您好,登錄后才能下訂單哦!
JVM內存分區及GC知識點是什么,很多新手對此不是很清楚,為了幫助大家解決這個難題,下面小編將為大家詳細講解,有這方面需求的人可以來學習下,希望你能有所收獲。
本地方法棧:native方法調用時的方法調用棧,存儲本地棧幀
虛擬機方法棧:Java的棧,每個線程有一個線程調用棧,棧的元素是棧幀。棧幀包括:局部變量表、操作數棧、指向堆中對象的引用、返回地址、附加信息。每個方法調用時,回向當前指向的線程棧頂部壓入一個棧幀,棧幀的大小是固定的,虛擬機通過解析.class文件可以得知。
堆:堆是所有Java線程共享的一個內存區域,用于分配對象。
方法區:存儲類的信息(類名、方法信息、字段信息)、靜態變量、常量池。
程序計數器:用于記錄下一條要執行的指令,每個線程都有自己的程序計數器,配合線程棧用于在線程調度時的線程上下文切換。執行本地方法時程序計數器中沒有值或為undefined。
1.8之前,在HotSpot中堆內存分為新生代、老年代、永久代。永久代就是方法區的實現,占用一部分堆內存,但永久代不參與垃圾回收。 1.8及置換,HotSpot將堆內存分為新生代、老年代。用元數據區實現方法區,且元數據區占用堆外內存。
引用計數:每個對象引用被持有時,引用計數+1。引用賦值為null或銷毀時引用計數-1。引用計數為0說明沒有再被使用,可以回收。無法解決循環引用問題。
標記-清除算法:第一個階段標記,將存活的對象標記出來;第二個階段是清除,將死亡對象占用的內存回收利用。
標記-整理算法:第一個節點標記,標記處存貨的對象;第二個階段整理,將存活的對象整理放到另一處空間中去,然后把當前空間的內存全部回收。
復制算法:每次只利用內存的一半,當內存滿時,將存貨對象復制到另一半中去,這一半內存全部回收。
垃圾回收算法并沒有最好的一種,為了達到降低整體GC時間的目的,一般是采用對內存分代進行垃圾回收,每個代采用不同的回收算法。
新生代 新生代與老年代內存占比默認為1:2。 新生代又可分為Eden區、Survivor0、Survivor1區,默認占用新生代內存比例為8:1:1。
老年代 老年代是一大塊連續內存,沒有再細分。
MinorGC,也叫做Young GC,YGC, 是發生在新生代的GC,當Eden區滿時觸發,HotSpot中新生代均GC采用復制算法實現。 新分配的對象一般是在Eden區,除非滿足條件時,對象會直接分配到堆上。Eden區滿之后會將Eden存活對象拷貝到survivor0區。當survivor0內存不足時,將survivor0中存活的對象拷貝到survivor1區,并回收survivor0區,然后交換survivor0和survivor1的名稱。一般也叫from和to。 每次從from到to的拷貝,都會記錄對象的年齡+1,當對象年齡達到一個設定的值后(默認15),對象會被分配到老年代。 如果從from向to的拷貝過程中發現to內存不夠用,也會將對象轉存到老年代。
直接老年代分配內存的情況:
對象所需內存大小超過Eden區大小,所有收集器都支持。
Eden區內存不足,且對象所需內存大小超過Eden區一半,直接分配到老年代,且不觸發MinorGC。使用ParallelScavenge時支持。
對象大小超過XX:PretenureSizeThreshold設置時,直接進入老年代分配。Serial、ParNew、CMS收集器支持。
FullGC FullGC是發生在老年代的GC,觸發時機有:
調用System.gc()時,系統會建議執行GC,但不是立即執行。
從新生代晉升到老年代的對象所需內存大于老年代可用內存時。
GC吞吐量 = 用戶代碼執行時間 / (用戶代碼執行時間 + GC時間)
jdk1.7和1.8默認采用ParallelGC,也就是Parallel Scavenge(新生代)+Parallel Old(老年代)GC。 jdk9采用G1垃圾收集器。
其他的垃圾收集器還有CMS、ParaNew、Serial等 查看java默認垃圾收集器命令:
java -XX:+PrintCommandLineFlags -version -XX:+UseParallelGC 表示使用Parallel Scavenge + Parallel Old -XX:+UseConcMarkSweepGC 在啟動應用加上這個參數表示使用CMS垃圾收集器 -XX:+UseG1GC 在啟動應用時添加這個參數表示使用G1垃圾收集器,jdk8中支持開啟
使用單線程進行垃圾回收,GC時停止用于線程工作。單CPU環境下表現好,因為沒有GC線程間的交互。 Serial用于新生代,復制算法。Serial Old用于老年代,基于標記-整理
Serial的多線程版本,采用復制算法。在單CPU時不如Serial,多CPU效果較好。 ParNew用于新生代。
Parallel Scavenge采用復制算法。算法目的是提高吞吐量,降低GC時間(但可能提升GC次數),讓用戶代碼得到更多執行時機。 Parallel代表并行,即多個GC線程同時進行,但還是會暫停用戶線程,在GC完成后用戶線程才繼續執行。 相比較于ParNew收集器,可以添加啟動參數XX+UseAdaptiveSizePolicy,添加參數后可以不用設置Eden和Survivor區比例,也不用設置晉升老年代對象年齡等細節參數,該算法會自動根據系統運行情況動態調節參數。
Parallel Old是Parallel Scavenge的老年代版本,采用標記-整理算法。可以并發GC,整個GC過程是暫停用戶線程執行的。
CMS全名Concurrent Mark Sweep,并發標記-回收垃圾收集器。是老年代的GC處理器。 CMS分為四個步驟:
初始標記:僅標記GCRoots和新生代能夠直接引用到的老年代對象,速度較快,觸發STW。
并發標記:此階段GC標記線程與用戶線程同時執行,遍歷初始標記的對象,并遞歸標記這些對象引用的全部對象,這個過程比較慢,但是不觸發STW。 因為這個階段是并發標記,可能發生 對象從新生代晉升到老年代、直接在老年代分配對象、老年代引用對象關系發生變化、新生代對老年代對象引用發生變化 等現象,對于這些對象都是要重新標記的。 做法是將這些對象所在的Card Table中的位設置為dirty,把并發標記階段新產生的對象和對象引用關系的變化記錄下來(記錄到Mod-Union Table)。
Card Table:將老年代內存分為相等大小的CardPage,每個CardPage用一個二進制位表示其內部的對象在并發標記階段是否發生了引用變化,這個二進制位數組就是CardTable。 Mod-Union Table:是一個和CardTable類似的結構; 3. 并發預清理:從Mod-Union Table中找到并發標記階段標記為dirty的內存區域,重新標記這些引用關系發生過變化的對象。可以通過參數CMSPrecleaningEnabled來關閉這個階段,默認開啟的。
4. 并發可中斷預清理:循環處理From和To區對象,標記可達的老年代對象;并且循環處理DirtyCard的標記,不觸發STW。循環的退出條件有三種
循環次數超過CMSMaxAbortablePrecleanLoops設置,默認0,沒有次數限制;
循環時間超過CMSMaxAbortablePrecleanTime設置,默認5s,超過會退出;
并發預清理時Eden使用率低于10%,而某一次循環后Eden使用率達到CMSScheduleRemarkEdenPenetration(默認50%)后會退出;
這個階段循環執行的目的是盡量減小下一個重新標記階段需要處理的新生代對象引用老年代對象的情況,因為下一個階段會觸發STW,為提高吞吐量自然是dirty狀態的內存越少越好。如果剛好在這個循環執行的5秒內觸發了YGC,然后這個階段又并發了處理了dirty的引用,則下個節點需要重新標記的對象就沒那么大,STW時間也就短了。 上一個并發預清理節點可以不要的原因就是這一步其實也能達到預清理的效果,而且是循環操作的。 5. 重新標記:較慢,CMS的瓶頸點,觸發STW,并發重新標記。雖然Preclean和AbortablePreclean已經盡量處理了DirtyCard,但是不能保證完全處理掉,因此還需要重新標記,要進行如下處理:
遍歷新生代對象,重新標記; 這個節點要遍歷新生代對象,如果新生代使用率很高,對象很多的話會耗時很久去遍歷,因此如果在進行重新標記之前觸發了YGC,這個步驟的耗時會減少很多。CMS可以通過參數CMSScavengeBeforeRemark來強制在重新標記階段之前進行一次YGC,該配置默認關閉。開啟的話可能會出現連續的兩次YGC,也挺浪費時間。 進行一次YGC之后并不是要把DirtyCard交給重新標記階段的5.3執行,而是交給4可中斷預處理來執行。
遍歷GCRoots,重新標記;
遍歷DirtyCard,重新標記,這時候經過前兩個階段的處理,DirtyCard已經減少了很多。
并發清除:根據標記結果清除垃圾對象,不觸發STW,速度較慢。
并發重置:重置CMS內部的數據,如CardTable、Mod-Uniod Table等,為下一次GC做準備,不觸發STW。
CMS的兩個標記階段,都會觸發STW,不一樣之處在于,初始標記僅標記GCRoots和新生代可達的老年代對象,沒有再遞歸遍歷標記,較快完成;而重新標記階段,標記GCRoots、新生代可達、DirtyCard中的老年代對象,且遞歸遍歷標記,比較耗時。
看完上述內容是否對您有幫助呢?如果還想對相關知識有進一步的了解或閱讀更多相關文章,請關注億速云行業資訊頻道,感謝您對億速云的支持。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。