編譯
編譯是指生成機器碼 即 二進制目標文件的過程。
Java最初是轉換爲類文件,虛擬機將其轉爲字節碼。
運行時動態的轉爲機器碼。
JIT會在運行時,將調用次數超過閾值CompileThreshold的代碼編譯(由方法調用計數器計數)。
JVM性能監控
重要的垃圾收集數據
堆大小。
新生代、老年代、永久代大小
Minor GC /Full GC 的持續時間、頻率、空間回收量
打印垃圾收集信息 -XX+PrintGCDetails
可以啓動時開啓, 也可用jinfo 動態開啓
常用GC啓動參數
推薦 -XX:+PrintGCDetails -XX:+PrintGCDateStamps-Xloggc:<filename> -XX:+UseConcMarkSweepGC -Xmx400m -Xms400m -Xmn30m -XX:+PrintGCDetails -XX:+PrintGCDateStamps -Xloggc:G:/學習/gclog.log -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=G:/學習/dump.hprof
資料:https://www.oracle.com/technetwork/java/javase/tech/vmoptions-jsp-140102.html
-XX:PrintGCTimeStamps 打印gc的時候打印時間戳
-XX:+UseConcMarkSweepGC 使用cms垃圾回收器
-Xmn30m 新生代30M 默認 SurvivorRatio 8, eden:s0:s1爲8:1:1,所以新生代爲9,即30m*0.9=27m
MetaspaceSize 爲本地內存。 非堆。
-Xmx400m -Xms400m 最大堆內存 400M,最小堆內存400M, 老年代=400m-30m=370m
jmap -head jpsid
收集的時候打印時間戳
-XX:+PrintGCTImeStamps 啓動秒數
-XX:+PrintGCDateStamps 時間戳
堆
內存分佈
圖片來源於網絡
Memory Pools
新生代
Minor GC
GC後 Eden幾乎是空的。
大多數新對象分配在Eden,大對象可能直接分配到老年代。
MonorailGC後 ,兩個Survivor交換角色。
“足夠老”的存活對象提升到老年代。
用到複製算法。
MinorGC過程中,Survivor(to)可能不足以容納Eden+另一個Survivor(from)中的存活對象。此時就需要將Surviv偶然中的存貨對象溢出?多餘的對象轉移到老年代–過早提升(Premature Promotion)。 — 如果老年代滿了,無法容納更大對象,minor GC後會導致Full GC.
Minor GC:Eden+to 存活對象–> to 不夠? ----> 老年代 不夠? ----> Full GC
老年代
“足夠老”的存活對象提升到老年代。
永久代
類加載 ,將類的源數據信息加載到永久代(jdk8以下)
永久代滿,引發垃圾收集-Full GC.
當需要加載其他類而空間不足,未使用的類就會從永久代中被卸載。
永久代垃圾回收不是stop-the-world
相關參數: -XX:PermSize 、 -XX:MaxPerSize
垃圾回收器
分代垃圾收集器
基於以下觀察事實:
- 大多數分配對象朝生夕死。
- 存活時間久的對象很少引用存活時間短的對象。
分代名稱 | 內存佔用 | 垃圾收集頻率 | 特點 |
---|---|---|---|
新生代 | 小 | 高 | minorGC,多次躲過回收後晉升到老年代 |
– | – | – | – |
老年代 | 大 | 低 | FullGC執行頻率低,但是執行時間長。儘量 |
– | – | – | – |
永久代 | 低 | 廢棄常量和無用類 |
Serial收集器
只使用一個處理器
minor GC 、 Full GC都 stop-the-world.
標記清除、標記壓縮
適用於 對停頓時間要求不高,多個JVM實例在一臺機器上。
Parellel收集器-吞吐量
吞吐量大。
盡最大可能收集。
Minor GC 並行。
Full GC 並行。
CMS收集器-低延遲
停頓少,低延遲
減少stop-the-world時間。
初始標記 a | 併發標記 b | 重新標記 c | 併發清除 d |
---|---|---|---|
短暫停頓,標記GC Roots可達對象① | 標記可從①對象達到的存活對象 | 再次停頓,重新標記b階段導致未被標記的存活對象 | 清除整個Java堆 |
G1收集器-分代、垃圾優先
分代
優先回收垃圾最多的區域。
JVM性能調優
關注穩定狀態下 內存使用,程序延遲,吞吐量
選擇JVM運行模式
client、server
垃圾收集器
吞吐量、延遲、內存佔用 三選二
調整內存大小
調整JVM堆佈局
調整新生代、老年代、永久代大小
初始堆空間大小配置
名稱 | 設置參數 | 說明 |
---|---|---|
Java堆 | -Xms和-Xmx | 3-4倍FullGC後老年代空間量 |
– | – | – |
永久代 | -XX:permSize和-XX:MaxPermSize | 1.2-1.5倍~ |
– | – | – |
新生代 | -Xmn | 1-1.5倍~ |
– | – | – |
老年代 | Java堆大小減新生代大小 | 2-3倍~ |
Survivor
Survivor太小,導致to無法存放所有從Eden空間和“From”空間複製來的活躍對象,導致提升到老年代。 加速老年代內存消耗,提前Full GC.
-XX:SurvivorRatio=<ratio>
ratio表示單個survivor空間與Eden空間大小的比率。
如果是8,則表示 from:to:eden = 1:1:8
所以調大Survivor需要降低ratio
survivor空間大小 = -Xmn/(-XX:SurvivorRatio=+2)
晉升
對象提升至老年代。
晉升閾值就是對象年齡。對象的年齡就是它所經歷的Minor GC次數。
【最大晉升閾值參數】:-XX:MaxTenuringThreshold=<n>
對象年齡超過n值將其提升到老年代。
不建議將【最大晉升閾值】設置爲0,這會導致剛分配的對象,會在接下來的Minor GC中直接從新生代晉升到老年代,導致老年代空間迅速增長以至於Full GC。
不成熟的(未到年齡)的晉升解決方案是:使用SurvivorRatio增大Survivor空間
當Survivor空間比較緊張,JVM會使用一個低於 最大晉升閾值(MaxTenuringThreshold)的值來保證目標Survivor空間的佔用。
注意:Survivor空間過小,導致JVM 內部計算的閾值過小,導致易晉升到老年代,導致Full GC頻繁。
監控晉升閾值
參數:-XX:+PrintTenuringDistribution
期望Survivor大小 小於 活下來的對象。導致Survivor空間溢出,提升對象到老年代。
至少將Survivor增大到 【存活的Survivor大小】
調整Survivor空間的容量
原則:調整Survivor空間的時候,如果新生代空間大小不變,增大Survivor則減少Eden。
因此,增大Survivor的同時,保持Eden空間不變,需要增大新生代空間。
理想狀態:晉升閾值等於最大晉升閾值
如果Minor GC時間過長,就要減少新生代空間大小。
調整目標Survivor空間佔用
嘗試Minor GC之後仍然維持的Survivor空間佔用
參數:-XX:TargetSurvivorRatio=<percent>