JVM內存模型及垃圾收集器介紹

一、JVM的內存模型

在這裏插入圖片描述
Metaspace:Class、Package、Method、Field、字節碼、常量池、符號引用等
CCS:32位指針的Class,沒開啓短指針壓縮的話不會有這個。-XX:+UseCompressedClassPointers
CodeCache:JIT編譯後的本地代碼,JNI使用的C代碼。開啓編譯執行纔有。-Xint=解釋執行。

運行時數據區

程序計數器PC Register
JVM支持多線程同時執行,每一個線程都有自己的程序計數器,線程正在執行的方法叫做當前方法,如果是Java代碼,程序計數器裏存放的就是當前正在執行的指令的地址。

虛擬機棧JVM Stacks
Java虛擬機棧是線程私有的,它的生命週期與線程相同。虛擬機棧描述的事Java方法執行的內存模型,每個方法在執行的同時會創建一個棧幀,用於存儲局部變量表、操作數棧、動態鏈接、方法出口等信息。每一個方法從調用直至執行完成的過程,就對應着一個棧幀在虛擬機棧中入棧到出棧的過程。

堆Heap
Java堆是Java虛擬機管理的內存中最大的一塊。堆是被所有線程共享的一塊區域,在虛擬機啓動時創建。此內存區域的唯一目的是存放對象實例,幾乎所有的對象實例都在這裏分配內存、Java堆可以處理物理上不連續的內存空間。只要邏輯上是連續的即可。

方法區Method Area

方法區與Java堆一樣。是各個線程共享的內存區域,用於存儲已被虛擬機加載的類信息,常量,靜態變量,即時編譯器編譯後的代碼等數據。雖然Java虛擬機規範把方法區描述爲堆的一個邏輯部分,但是它有一個別名Non-Heap(非堆),與Java堆區分開來。
運行時常量池 RunTime Constant Pool。是方法區的一部分。除了有Class文件中類的版本、字段、方法、接口等描述信息外。還有一項信息是常量池( Constant Pool),用於存放編譯期生成的各種字面量和符號引用,這部分內容在類加載後進入方法區的運行時常量池中存放。

本地方法棧

本地方法棧與虛擬機棧所發揮的作用是類似的。他們之間區別不過是虛擬機棧作爲虛擬機執行Java方法服務,而本地方法棧則爲虛擬機使用到Natice方法服務。


二、垃圾回收算法

Java採用枚舉根節點,做可達性分析來進行垃圾回收。
根節點:類加載器,Thread、虛擬機棧的本地變量表、static成員、常量引用、本地方法棧的變量等待

1.標記清除算法
首先標記出所有需要回收的對象,在標記完成後統一回收所有對象。
缺點:效率不高。算法效率低。產生內存碎片,碎片太多會提前導致GC。

2.複製算法 把內存劃分爲大小相等的2塊,每次只使用一塊。當一塊內存用完了,把存活對象拷貝到另一塊上面。再把這一塊一次性全部清除。 缺點:空間利用率低。

3.標記整理 類似標記清除,不過清理步驟不同。讓所有存活對象向一端移動,然後直接清理掉端邊界以外的內存。 缺點:整理內存耗時。

Java採用分代垃圾回收(JDK11的ZGC就不是分代回收了)
Young區採用複製算法,因爲Young區的對象生命週期大多比較短。
Old區採用標記整理或標記清除,Old區的對象大多生命週期大多比較長。如果採用複製算法,複製來複制去都存在,效率不高。

Java中對象內存分配過程
優先在Eden區分配
大對象直接進入老年代,定義大對象的大小參數:-XX:PretenureSizeThreshold
長期存活對象進入老年代。多次在YGC存活下來的對象。發生一次,這個對象的年齡+1。定義次數:-XX:MaxTenuringThreshold,垃圾回收之後Survivor大小比例達到這個值-XX:TargetSurvivorRatio,就計算對象的平均年齡是否超過-XX:MaxTenuringThreshold。超過的話也會轉移到Old區。-XX:PrintTenuringDistribution發生YGC時打印對象的年齡情況


三、垃圾收集器

串行的Serial(單個垃圾回收線程):Serial、Serial Old
並行的Parallel(多個垃圾回收線程):Parallel Scavenge、Parallel Old,吞吐量
併發的(多個垃圾回收線程):CMS,G1,停頓時間

並行:多個垃圾收集器線程並行工作,但此時用戶線程仍處於等待狀態。適合科學計算,後臺處理等弱交互場景。
併發:指在執行垃圾回收線程的時候可以同時執行用戶線程。不影響用戶線程的運行。適合Web交互應用。
停頓時間:垃圾收集器在做垃圾回收的時候中斷應用執行的時間,-XX:MaxGCPauseMillis
吞吐量:花在垃圾收集的時間和花在應用時間的佔比。-XX:GCTimeRatio=,垃圾收集時間佔:1/1+n

串行收集器,不適用
-XX:+UseSerialGC -XX:+UseSerialOldGC
並行收集器,吞吐量優先,默認關閉的。可以動態調整內存
-XX:+UseParallelGC  -XX:+UseParallelOldGC 
-XX:ParallelGCThreads=<N> 開啓N個GC線程,CPU>8核,默認開啓5/8,CPU<8,默認N=CPU
-XX:MaxGCPauseMillis=<N> 最大GC停頓時間
-XX:GCTimeRatio=<N> 吞吐量
-Xmx<N> 最大堆大小
並行收集器會自動根據這些參數調整堆的大小,優先滿足停頓時間的要求,吞吐量的要求
JVM的動態內存調整的參數
-XX:YoungGenerationSizeIncrement=<Y> Young區默認自動調大比率,默認20%
-XX:TenuredGenerationSizeIncrement=<Y> Old區默認自動調大比率,默認20%
-XX:AdaptiveSizeDecrementScaleFactor=<Y> 默認自動調小的比率,默認4%
JVM可以通過這些參數來動態調整堆的大小
併發收集器,響應時間優先
併發收集,可以與應用線程同時執行。低停頓,低延遲。

---
CMS:老年代收集器。開啓:-XX:+UseConcMarkSweepGC  -XX:+UseParNewGC 
回收過程:
CMS initial Mark:初始標記GC Root,STW
CMS concurrent Mark:併發標記,從根節點往下開始標記。非STW
CMS-concurrent-preclean:併發預清理
CMS remark:重新標記,STW
CMS concurrent sweep:併發清除
CMS-concurrent-reset:併發重置
缺點:
CPU敏感
浮動垃圾,在GC過程中應用程序線程還在運行,可能會產生垃圾,產生空間碎片
相關參數:
-XX:ConcGCThreads:併發GC線程數
-XX:+UseCMSCompactAtFullCollection:FullGC之後做內存壓縮
-XX:CMSFullGCsBeforeCompaction:多少次FullGC之後壓縮一次  最好是1
-XX:CMSInitiatingOccupancyFraction:Old區佔滿多少纔會觸發FullGC,默認92%
計算公式 <=((Xmx-Xmn)-(Xmn-Xmn/(SurvivorRatior+2)))/(Xmx-Xmn)*100   53
-XX:+UseCMSInitiatingOccupancyOnly:上面這個參數時候動態可調
-XX:+CMSScavengeBeforeRemark:FullGC之前先做YGC

---
G1:年輕代老年代都可以用的收集器。開啓:-XX:+UseG1GC
SATB:通過Root Tracing得到的,GC開始的時候存活對象的快照。
RSet:記錄了其他Region中的對象引用本Region中對象的關係。標記誰引用了我的對象。
 
G1裏沒有FullGC概念。叫MixedGC。回收所有的Young和部分的Old。 
回收過程: 
global concurrent marking:全局併發標記
Initial marking phase:標記GC Root,STW
Root region scanning phase:標記存活Region
Concurrent marking phase:標記存活對象
Remark phase:重新標記,STW
Cleanup phase:部分STW

MixedGC時機:InitiatingHeapOccupancyPercent:堆佔有率達到這個比率觸發global concurrent marking。默認45%
在global concurrent marking結束之後,可以知道內存區有多少空間要被回收,在每次YGC之後和再次發生MixedGC之前,
會檢查垃圾佔比是否達到G1HeapWastePersent,只有達到了,下次纔會發生MixedGC。

相關參數:
G1MixedGCLiveThresholdPersent:Old區的Region被回收的時候的存活對象佔比
G1MixedGCCountTarget:一次global concurrent marking之後,最多執行MixedGC的次數
G1OldCSetRegionThresholdPersent:一次MixedGC最多回收多少Old區的Region數量
-XX:G1HeapRegionSize=<N> Region的大小,1-32M,最多2048個
-XX:MaxGCPauseMillis=<N> 最大GC停頓時間
-XX:G1NewSizePersent -XX:G1MaxNewSizePersent:Young區的堆大小比率
-XX:G1ReservePersent=10:保留,防止YGC拷貝對象時內存不夠
-XX:ParallelGCThreads=n:停止應用程序,STW時的線程數。執行標記,清除的時候的線程
-XX:ConcGCThreads=n:併發線程數=1/4*並行

最佳實踐:
年輕代大小:避免使用-Xmn、-XX:NewRatio等顯示設置Young區大小,會覆蓋暫停實踐目標暫停實踐目標:
暫停時間不要太嚴苛,其吞吐量目標是90%的應用程序時間和10%的垃圾回收時間,太嚴苛會影響到吞吐量

G1把內存分爲多個Region,不超過2048個。Young和Old是邏輯上的概念。H用來存放大對象。
在這裏插入圖片描述

垃圾收集器的搭配使用
在這裏插入圖片描述

如何選擇垃圾收集器

優先調整堆的大小讓服務器自己來選擇
如果內存小於100M,使用串行的收集器
如果是單核CPU,並且沒有停頓時間的要求,串行或者JVM自己選擇
如果允許停頓時間超過1秒,選擇並行或者JVM自己選擇
如果響應時間最重要,並且不能超過1秒,使用併發收集器


在這裏插入圖片描述


  • 我的公衆號:Coding摳腚
  • 人生海海,山山而川。
    Coding摳腚
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章