JVM日志參數十全大補丸
作者丨安琪拉的博客
來源丨安琪拉的博客
快吃下這顆JVM十全大補丸,媽媽再也不擔心JVM日志看不懂了!
本文需要一些JVM的儲備知識,如果對JVM內存區(qū)域還不熟悉,可以看看基礎知識部分,在JVM群里提問,評論區(qū)留言都可以,安琪拉玩家都很熱心,社區(qū)很隨意。
實踐JVM日志
我們先打印點GC日志實踐一下,再開始講后面的理論。
首先在IDEA 中設置打印GC的參數,比如我設置的參數如下,堆、新生代老年代都設置的比較小,這樣比如容易出GC日志。
-Xms56m -Xmx56m -Xmn21m -Xss512k -XX:MetaspaceSize=12m -XX:MaxMetaspaceSize=12m -verbose:gc -XX:+PrintGCDetails -XX:+PrintGCDateStamps

寫一段普通的程序,for循環(huán)往Map里面塞東西,然后主動觸發(fā)GC。
public class GCLogTest {
private static Map<String, String> mapContainer = new HashMap<>();
public static void main(String[] args) {
String stringDataPrefix = "key_prefix_";
for (int i = 0; i < 3000000; i++) {
String newStringData = stringDataPrefix + i;
mapContainer.put(newStringData, newStringData);
}
System.out.println("MAP size: " + mapContainer.size());
System.gc(); // 明顯的GC
// 移除 2/3
for (int i = 0; i < 2000000; i++) {
String newStringData = stringDataPrefix + i;
mapContainer.remove(newStringData);
}
System.out.println("MAP size: " + mapContainer.size());
System.gc();
System.out.println("End of program!");
}
}
我們開始講解GC日志,下面是完整的GC日志,我們分段講解。

JVM問題場景
一般JVM平常大家不會去關注,一出問題往往還都是大問題,比如線上突然FGC徒增啦!下面講講常見的幾種JVM問題:
年輕代:young gc時間很長,比如十幾秒,甚至幾十秒,這種都是很不正常,一般是swap打開了。
年輕代:young gc過于頻繁,一般調大堆或者年輕代,或者把Eden區(qū)域和Survivor區(qū)域比重調一下能解決。
FullGC過于頻繁一般是調大堆或者老年代能解決,當然前期是沒有內存泄漏。
內存碎片過多觸發(fā)cms,可以在凌晨業(yè)務低峰的時候主動觸發(fā)Full GC。
程序本身代碼有問題,導致內存泄露,或者gc嚴重,甚至oom,可以dump內存快照,自己通過MAT等工具分析,如果希望社區(qū)幫忙排查,丟到JVM群大家一起分析。
JVM參數大全
下面講解一下常見的一些JVM 參數,吃下這顆十全大補丸。
整個堆大小 = 年輕代大小 + 年老代大小 + 持久代大小. JDK1.8以后沒有持久代,而是換成了Metaspace(元數據),這塊空間不屬于堆。
常見的配置項先列出來:
-server : 服務器模式,JVM有客戶端和服務器二種模式,最主要的差別在于:-Server模式啟動時,速度較慢,但是一旦運行起來后,性能將會有很大的提升。JVM工作在Server模式下可以大大提高性能,Server模式下應用的啟動速度會比client模式慢大概10%,但運行速度比Client VM要快至少有10倍。-client : 客戶端模式,與上面的對應
-Xms:初始堆大小,默認大小是物理內存的1/64(<1GB),默認剩下的堆內存小于40%時,JVM就會增大堆直到-Xmx的最大限制,這個比例MinHeapFreeRatio參數可以調整。
-Xmx:最大堆大小,物理內存的1/4(<1GB),默認剩余堆內存大于70%時,JVM會縮小堆大小,直到 -Xms的最小限制這個比例可以通過MaxHeapFreeRatio參數調整。
-Xmn:年輕代大小(JDK >=1.4),Minor GC發(fā)生的地方。大小 = eden+ 2 survivor space。增大年輕代后, 將會減小年老代大小.此值對系統(tǒng)性能影響較大,Sun官方推薦配置為整個堆的3/8。
-XX:NewSize 也是年輕代大小 (JDK < 1.4),跟Xmn功能一樣。
-XX:PermSize :持久代初始值,JDK1.8 采用Metaspace(元數據),沒有這玩意了,默認值是物理內存的1/64。
-XX:MaxPermSize : 持久代最大值, 默認物理內存的1/4。
-Xss:每個線程的棧大小,都知道線程調用局部變量都在棧中,函數調用就是壓棧彈棧,JDK5.0 以后每個線程堆棧大小為1M, 以前每個線程堆棧大小為256K,應用一般根據自己的特性,進行調整,比如調用層次嵌套很深,可以設置大一點。在相同物理內存下,減小這個值能生成更多的線程.但是操作系統(tǒng)對一個進程內的線程數還是有限制的,不能無限生成,經驗值在3000~5000左右,一般小的應用, 如果棧不是很深, 應該是128k夠用的 大的應用建議使用256k。這個選項對性能影響比較大,需要嚴格的測試。
-XX:NewRatio :年輕代(包括Eden和兩個Survivor區(qū))與年老代的比值(除去持久代),-XX:NewRatio=4表示年輕代與年老代所占比值為1:4,年輕代占整個堆棧的1/5,一般大部分時候設置了 Xms=Xmx 并且設置了Xmn的情況下,該參數不需要進行設置,這個參數不那么需要被關注。
-XX:SurvivorRatio :Eden區(qū)與Survivor區(qū)的大小比值,設置為8, 則兩個Survivor區(qū)與一個Eden區(qū)的比值為2:8, 一個Survivor區(qū)占整個年輕代的1/10。
-XX:+DisableExplicitGC :禁止System.gc(),也就是你在程序中寫了System.gc()不會生效。
-XX:MaxTenuringThreshold :垃圾最大年齡,
-XX:+UseBiasedLocking :可以設置是否開啟鎖的偏向,關于鎖偏向,synchronized鎖膨脹會有,可以看我之前寫的 一個synchronized跟面試官扯了半個小時
-XX:PretenureSizeThreshold :對象超過多大是直接在舊生代分配
-XX:+CollectGen0First :FullGC時是否先YGC
收集器有關參數
并行收集器相關參數
-XX:+UseParallelGC :選擇垃圾收集器為并行收集器,此配置僅對年輕代有效
-XX:+UseParNewGC :設置年輕代為并行收集,可與CMS收集同時使用,jdk1.8建議才用 ParNew + CMS
-XX:ParallelGCThreads : 并行收集器的線程數, 一般等于CPU核數比較合適
-XX:+UseParallelOldGC :年老代垃圾收集方式為并行收集(Parallel Compacting)
-XX:MaxGCPauseMillis :每次年輕代垃圾回收的最長時間(最大暫停時間)
-XX:GCTimeRatio :設置垃圾回收時間占程序運行時間的百分比
-XX:+ScavengeBeforeFullGC :Full GC前調用YGC
CMS相關參數
-XX:+UseConcMarkSweepGC :使用CMS內存收集,如果用CMS,一般就用xmn參數設置年輕代,不用-XX:NewRatio等。注意最新的JVM版本,當使用-XX:+UseConcMarkSweepGC時,-XX:UseParNewGC會自動開啟。因此,如果年輕代的并行GC不想開啟,可以通過設置-XX:-UseParNewGC來關掉。
-XX:CMSFullGCsBeforeCompaction :多少次后進行內存壓縮,由于并發(fā)收集器不對內存空間進行壓縮, 整理, 所以運行一段時間以后會產生"碎片",使得運行效率降低.此值設置運行多少次GC以后對內存空間進行壓縮,整理。
-XX+UseCMSCompactAtFullCollection :在FULL GC的時候, 對年老代的壓縮。CMS是不會移動內存的, 因此, 這個非常容易產生碎片, 導致內存不夠用, 因此, 內存的壓縮這個時候就會被啟用。增加這個參數是個好習慣??赡軙绊懶阅? 但是可以消除碎片。
-XX:+UseCMSInitiatingOccupancyOnly :命令JVM不基于運行時收集的數據來啟動CMS垃圾收集周期,使用手動定義初始化,定義開始CMS收集,JVM通過CMSInitiatingOccupancyFraction的值進行每一次CMS收集,而不僅僅是第一次。
-XX:CMSInitiatingOccupancyFraction :默認為68,即當年老代的空間使用率達到68%時,會執(zhí)行一次CMS回收。如果應用程序的內存使用率增長很快,可以根據應用特點,可以對該值進行調優(yōu),如果內存增長緩慢,則可以設置一個稍大的值,大的閾值可以有效降低CMS的觸發(fā)頻率,減少年老代回收的次數可以較為明顯地改善應用程序性能。反之,如果應用程序內存使用率增長很快,則應該降低這個閾值,以避免頻繁觸發(fā)年老代串行收集器。
額外配置信息
-XX:+PrintGC 打印GC,輸出形式:[GC 16384K->2544K(18944K), 0.0094143 secs] 、[Full GC 16384K->2544K(18944K), 0.0650971 secs]
-XX:+PrintGCDetails 打印詳細GC,這個參數我們上面例子已經用過了。
-XX:+PrintGCTimeStamps 這個就是上面的格式打印GC的時間戳,格式參考:2021-04-01T23:35:41.454-0800
-XX:+PrintGCApplicationStoppedTime 打印垃圾回收期間程序暫停的時間
-XX:+PrintGCApplicationConcurrentTime 打印每次垃圾回收前,程序未中斷的執(zhí)行時間
后續(xù)
昨天JVM群里有人貼出了一些GC日志截圖,大家興致很高,就寫了這篇日志查看文給大家掃清看日志的障礙。
大家感興趣可以加我微信:guofu-angela ,JVM群學習群還有坑位。
現(xiàn)在微信推送機制改了,為了防止大家漏看每天的推送,建議星標一下安琪拉的博客。
-End-
最近有一些小伙伴,讓我?guī)兔φ乙恍?nbsp;面試題 資料,于是我翻遍了收藏的 5T 資料后,匯總整理出來,可以說是程序員面試必備!所有資料都整理到網盤了,歡迎下載!

面試題】即可獲取