如果你對GC過程不熟悉,建議先看上一篇文章:GC-垃圾回收站 http://www.reibang.com/p/4ba980563ec0
什么情況下需要進行jvm調(diào)優(yōu)掘托?
1.Heap內(nèi)存(老年代)持續(xù)上漲達到設(shè)置的最大內(nèi)存值瘦锹;
2.Full GC 次數(shù)頻繁;
3.GC 停頓時間過長(超過1秒)烫映;
4.應(yīng)用出現(xiàn)OutOfMemory 等內(nèi)存異常沼本;
5.應(yīng)用中有使用本地緩存且占用大量內(nèi)存空間;
6.系統(tǒng)吞吐量與響應(yīng)性能不高或下降锭沟。
第3點:使用Java的內(nèi)存分析工具抽兆,如JVisualVM、JMC或JProfiler族淮。這些工具可以幫助您分析Java堆內(nèi)存中的對象辫红,并找出哪些對象占用了大量的內(nèi)存。您可以使用這些工具來檢查應(yīng)用程序的本地緩存祝辣,并查看緩存中存儲的數(shù)據(jù)贴妻。
JVM調(diào)優(yōu)的基本原則
JVM調(diào)優(yōu)是一個手段,但并不一定所有問題都可以通過JVM進行調(diào)優(yōu)解決蝙斜,因此名惩,在進行JVM調(diào)優(yōu)時,我們要遵循一些原則:
- 大多數(shù)的Java應(yīng)用不需要進行JVM優(yōu)化孕荠;
- 大多數(shù)導(dǎo)致GC問題的原因是代碼層面的問題導(dǎo)致的(代碼層面)娩鹉;
- 上線之前攻谁,應(yīng)先考慮將機器的JVM參數(shù)設(shè)置到最優(yōu);
- 減少創(chuàng)建對象的數(shù)量(代碼層面)弯予;
- 減少使用全局變量和大對象(代碼層面)戚宦;
- 優(yōu)先架構(gòu)調(diào)優(yōu)和代碼調(diào)優(yōu),JVM優(yōu)化是不得已的手段(代碼锈嫩、架構(gòu)層面)受楼;
- 分析GC情況優(yōu)化代碼比優(yōu)化JVM參數(shù)更好(代碼層面);
通過以上原則呼寸,我們發(fā)現(xiàn)艳汽,其實最有效的優(yōu)化手段是架構(gòu)和代碼層面的優(yōu)化,而JVM優(yōu)化則是最后不得已的手段等舔,也可以說是對服務(wù)器配置的最后一次“壓榨”骚灸。
JVM調(diào)優(yōu)目標(biāo)
調(diào)優(yōu)的最終目的都是為了令應(yīng)用程序使用最小的硬件資源消耗來承載更大的吞吐糟趾。jvm調(diào)優(yōu)主要是針對垃圾收集器的收集性能優(yōu)化慌植,令運行在虛擬機上的應(yīng)用能夠使用更少的內(nèi)存以及延遲獲取更大的吞吐量。
延遲:GC低停頓和GC低頻率义郑;
低內(nèi)存占用蝶柿;
高吞吐量;
其中,任何一個屬性性能的提高非驮,幾乎都是以犧牲其他屬性性能的損為代價的交汤,不可兼得。具體根據(jù)在業(yè)務(wù)中的重要性確定劫笙。
注釋:GC停頓(GC pause)是指在垃圾回收過程中芙扎,暫停所有應(yīng)用程序線程的時間。這個停頓是為了讓垃圾回收器能夠有效地識別和回收不再使用的對象填大,從而釋放內(nèi)存空間戒洼。雖然GC停頓可能會影響應(yīng)用程序的性能,但一些垃圾收集器采用了降低停頓時間和提高吞吐量的優(yōu)化技術(shù)允华,如并行垃圾回收和增量標(biāo)記等圈浇。這些技術(shù)可以減少GC停頓對應(yīng)用程序的影響,從而提高系統(tǒng)的性能和響應(yīng)速度靴寂。
GC低停頓和GC低頻率:即降低GC的停頓時間 或 降低GC的頻率
JVM調(diào)優(yōu)量化目標(biāo)
下面展示了一些JVM調(diào)優(yōu)的量化目標(biāo)參考實例:
- Heap 內(nèi)存使用率 <= 70%;
Old generation內(nèi)存使用率<= 70%;
avgpause <= 1秒;
Full gc 次數(shù)0 或 avg pause interval >= 24小時 ;
注意:不同應(yīng)用的JVM調(diào)優(yōu)量化目標(biāo)是不一樣的磷蜀。
調(diào)優(yōu)工具 (參考GCViewer文章)
借助GCViewer日志分析工具,可以非常直觀地分析出待調(diào)優(yōu)點百炬『致。可從以下幾方面來分析:
Memory,分析Totalheap、Tenuredheap剖踊、Youngheap內(nèi)存占用率及其他指標(biāo)庶弃,理論上內(nèi)存占用率越小越好轨蛤;
Pause,分析Gc pause虫埂、Fullgc pause祥山、Total pause三個大項中各指標(biāo),理論上GC次數(shù)越少越好掉伏,GC時長越小越好缝呕;
JVM調(diào)優(yōu)的步驟
一般情況下,JVM調(diào)優(yōu)可通過以下步驟進行:
- 分析GC日志及dump文件斧散,判斷是否需要優(yōu)化供常,確定瓶頸問題點;
- 確定JVM調(diào)優(yōu)量化目標(biāo)鸡捐;
- 確定JVM調(diào)優(yōu)參數(shù)(根據(jù)歷史JVM參數(shù)來調(diào)整)栈暇;
- 依次調(diào)優(yōu)內(nèi)存、延遲箍镜、吞吐量等指標(biāo)源祈;
- 對比觀察調(diào)優(yōu)前后的差異;
- 不斷的分析和調(diào)整色迂,直到找到合適的JVM參數(shù)配置香缺;
- 找到最合適的參數(shù),將這些參數(shù)應(yīng)用到所有服務(wù)器歇僧,并進行后續(xù)跟蹤图张。
以上操作步驟中,某些步驟是需要多次不斷迭代完成的诈悍。一般是從滿足程序的內(nèi)存使用需求開始的祸轮,之后是時間延遲的要求,最后才是吞吐量的要求侥钳,要基于這個步驟來不斷優(yōu)化适袜,每一個步驟都是進行下一步的基礎(chǔ),不可逆行之慕趴。
JVM參數(shù)
JVM調(diào)優(yōu)最重要的工具就是JVM參數(shù)了痪蝇。先來了解一下JVM參數(shù)相關(guān)內(nèi)容。
-XX 參數(shù)被稱為不穩(wěn)定參數(shù)冕房,此類參數(shù)的設(shè)置很容易引起JVM 性能上的差異躏啰,使JVM存在極大的不穩(wěn)定性。如果此類參數(shù)設(shè)置合理將大大提高JVM的性能及穩(wěn)定性耙册。
不穩(wěn)定參數(shù)語法規(guī)則包含以下內(nèi)容给僵。
布爾類型參數(shù)值:
-XX:+ ‘+’表示啟用該選項
-XX:- ‘-‘表示關(guān)閉該選項
數(shù)字類型參數(shù)值:
-XX:=給選項設(shè)置一個數(shù)字類型值,可跟隨單位,例如:’m’或’M’表示兆字節(jié);’k’或’K’千字節(jié);’g’或’G’千兆字節(jié)帝际。32K與32768是相同大小的蔓同。
字符串類型參數(shù)值:
-XX:=給選項設(shè)置一個字符串類型值,通常用于指定一個文件蹲诀、路徑或一系列命令列表斑粱。例如:-XX:HeapDumpPath=./dump.core
JVM參數(shù)解析及調(diào)優(yōu)
比如以下參數(shù)示例:
-Xmx4g –Xms4g –Xmn1200m –Xss512k -XX:NewRatio=4 -XX:SurvivorRatio=8 -XX:PermSize=100m -XX:MaxPermSize=256m -XX:MaxTenuringThreshold=15
上面為Java7及以前版本的示例,在Java8中永久代的參數(shù)-XX:PermSize和-XX:MaxPermSize已經(jīng)失效脯爪。這在前面章節(jié)中已經(jīng)講到则北。
參數(shù)解析:
-Xmx4g:堆內(nèi)存最大值為4GB。
-Xms4g:初始化堆內(nèi)存大小為4GB痕慢。
-Xmn1200m:設(shè)置年輕代大小為1200MB尚揣。增大年輕代后,將會減小年老代大小掖举。此值對系統(tǒng)性能影響較大快骗,Sun官方推薦配置為整個堆的3/8。
-Xss512k:設(shè)置每個線程的堆棧大小塔次。JDK5.0以后每個線程堆棧大小為1MB方篮,以前每個線程堆棧大小為256K。應(yīng)根據(jù)應(yīng)用線程所需內(nèi)存大小進行調(diào)整俺叭。在相同物理內(nèi)存下恭取,減小這個值能生成更多的線程泰偿。但是操作系統(tǒng)對一個進程內(nèi)的線程數(shù)還是有限制的熄守,不能無限生成,經(jīng)驗值在3000~5000左右耗跛。
-XX:NewRatio=4:設(shè)置年輕代(包括Eden和兩個Survivor區(qū))與年老代的比值(除去持久代)裕照。設(shè)置為4,則年輕代與年老代所占比值為1:4调塌,年輕代占整個堆棧的1/5
-XX:SurvivorRatio=8:設(shè)置年輕代中Eden區(qū)與Survivor區(qū)的大小比值晋南。設(shè)置為8,則兩個Survivor區(qū)與一個Eden區(qū)的比值為2:8羔砾,一個Survivor區(qū)占整個年輕代的1/10
-XX:PermSize=100m:初始化永久代大小為100MB负间。
-XX:MaxPermSize=256m:設(shè)置持久代大小為256MB。
-XX:MaxTenuringThreshold=15:設(shè)置垃圾最大年齡姜凄。如果設(shè)置為0的話政溃,則年輕代對象不經(jīng)過Survivor區(qū),直接進入年老代态秧。對于年老代比較多的應(yīng)用董虱,可以提高效率。如果將此值設(shè)置為一個較大值,則年輕代對象會在Survivor區(qū)進行多次復(fù)制愤诱,這樣可以增加對象再年輕代的存活時間云头,增加在年輕代即被回收的概論。
新生代淫半、老生代溃槐、永久代的參數(shù),如果不進行指定科吭,虛擬機會自動選擇合適的值竿痰,同時也會基于系統(tǒng)的開銷自動調(diào)整。
可調(diào)優(yōu)參數(shù):
-Xms:初始化堆內(nèi)存大小砌溺,默認為物理內(nèi)存的1/64(小于1GB)影涉。
-Xmx:堆內(nèi)存最大值。默認(MaxHeapFreeRatio參數(shù)可以調(diào)整)空余堆內(nèi)存大于70%時规伐,JVM會減少堆直到-Xms的最小限制蟹倾。
-Xmn:新生代大小,包括Eden區(qū)與2個Survivor區(qū)猖闪。
-XX:SurvivorRatio=1:Eden區(qū)與一個Survivor區(qū)比值為1:1鲜棠。
-XX:MaxDirectMemorySize=1G:直接內(nèi)存。報java.lang.OutOfMemoryError: Direct buffer memory異撑嗷牛可以上調(diào)這個值豁陆。
-XX:+DisableExplicitGC:禁止運行期顯式地調(diào)用System.gc()來觸發(fā)fulll GC。
注意: Java RMI的定時GC觸發(fā)機制可通過配置-Dsun.rmi.dgc.server.gcInterval=86400來控制觸發(fā)的時間吵护。
-XX:CMSInitiatingOccupancyFraction=60:老年代內(nèi)存回收閾值盒音,默認值為68。
-XX:ConcGCThreads=4:CMS垃圾回收器并行線程線馅而,推薦值為CPU核心數(shù)祥诽。
-XX:ParallelGCThreads=8:新生代并行收集器的線程數(shù)。
-XX:MaxTenuringThreshold=10:設(shè)置垃圾最大年齡瓮恭。如果設(shè)置為0的話雄坪,則年輕代對象不經(jīng)過Survivor區(qū),直接進入年老代屯蹦。對于年老代比較多的應(yīng)用维哈,可以提高效率。如果將此值設(shè)置為一個較大值登澜,則年輕代對象會在Survivor區(qū)進行多次復(fù)制阔挠,這樣可以增加對象再年輕代的存活時間,增加在年輕代即被回收的概論帖渠。
-XX:CMSFullGCsBeforeCompaction=4:指定進行多少次fullGC之后谒亦,進行tenured區(qū) 內(nèi)存空間壓縮。
-XX:CMSMaxAbortablePrecleanTime=500:當(dāng)abortable-preclean預(yù)清理階段執(zhí)行達到這個時間時就會結(jié)束。
在設(shè)置的時候份招,如果關(guān)注性能開銷的話切揭,應(yīng)盡量把永久代的初始值與最大值設(shè)置為同一值,因為永久代的大小調(diào)整需要進行FullGC才能實現(xiàn)锁摔。
內(nèi)存優(yōu)化示例
當(dāng)JVM運行穩(wěn)定之后廓旬,觸發(fā)了FullGC我們一般會拿到如下信息:
3.153:
[Full GC(Ergonomics)
[PSYoungGen:37887K->0k(359424K{新生代大小})]
[ParOldGen:84645K->93168K{FulIGC之后老年代空間占用}(184832K{老年代大小}]
122533K->93168K(544256K),
[Metaspace: 3135K->3135K{FulIGC后永久代空間占用}(1056768K{永久代大小})],
0.0773607 secs{FullGC時間}]
[Times: user=0.25 sys=0.02, real=0.07 secs]
以上gc日志中,在發(fā)生fullGC之時谐腰,整個應(yīng)用的堆占用以及GC時間孕豹。為了更加精確需多次收集,計算平均值十气±常或者是采用耗時最長的一次FullGC來進行估算。上圖中砸西,老年代空間占用在93168kb(約93MB)叶眉,以此定為老年代空間的活躍數(shù)據(jù)。則其他堆空間的分配芹枷,基于以下規(guī)則來進行衅疙。
java heap:參數(shù)-Xms和-Xmx,建議擴大至3-4倍FullGC后的老年代空間占用鸳慈。
永久代:-XX:PermSize和-XX:MaxPermSize饱溢,建議擴大至1.2-1.5倍FullGc后的永久帶空間占用。
新生代:-Xmn走芋,建議擴大至1-1.5倍FullGC之后的老年代空間占用绩郎。
老年代:2-3倍FullGC后的老年代空間占用。
空間 倍數(shù)
總大新唐浮:3-4 倍活躍數(shù)據(jù)的大小
新生代:1-1.5 活躍數(shù)據(jù)的大小
老年代:2-3 倍活躍數(shù)據(jù)的大小
永久代 :1.2-1.5 倍Full GC后的永久代空間占用
基于以上規(guī)則嗽上,則對參數(shù)定義如下:
java -Xms373m -Xmx373m -Xmn140m -XX:PermSize=5m -XX:MaxPermSize=5m
延遲優(yōu)化示例
對延遲性優(yōu)化,首先需要了解延遲性需求及可調(diào)優(yōu)的指標(biāo)有哪些熄攘。
應(yīng)用程序可接受的平均停滯時間: 此時間與測量的Minor
GC持續(xù)時間進行比較”四睿可接受的Minor GC頻率:Minor
GC的頻率與可容忍的值進行比較挪圾。
可接受的最大停頓時間:最大停頓時間與最差情況下FullGC的持續(xù)時間進行比較。
可接受的最大停頓發(fā)生的頻率:基本就是FullGC的頻率逐沙。
其中哲思,平均停滯時間和最大停頓時間,對用戶體驗最為重要吩案。對于上面的指標(biāo)棚赔,相關(guān)數(shù)據(jù)采集包括:MinorGC的持續(xù)時間、統(tǒng)計MinorGC的次數(shù)、FullGC的最差持續(xù)時間靠益、最差情況下丧肴,F(xiàn)ullGC的頻率。
吞吐量調(diào)優(yōu)
吞吐量調(diào)優(yōu)主要是基于應(yīng)用程序的吞吐量要求而來的胧后,應(yīng)用程序應(yīng)該有一個綜合的吞吐指標(biāo)芋浮,這個指標(biāo)基于整個應(yīng)用的需求和測試而衍生出來的。
評估當(dāng)前吞吐量和目標(biāo)差距是否巨大壳快,如果在20%左右纸巷,可以修改參數(shù),加大內(nèi)存眶痰,再次從頭調(diào)試瘤旨,如果巨大就需要從整個應(yīng)用層面來考慮,設(shè)計以及目標(biāo)是否一致了竖伯,重新評估吞吐目標(biāo)裆站。
對于垃圾收集器來說,提升吞吐量的性能調(diào)優(yōu)的目標(biāo)就是盡可能避免或者很少發(fā)生FullGC或者Stop-The-World壓縮式垃圾收集(CMS)黔夭,因為這兩種方式都會造成應(yīng)用程序吞吐降低宏胯。盡量在MinorGC 階段回收更多的對象,避免對象提升過快到老年代本姥。
最后肩袍,回到文章的第一個問題,如果出現(xiàn)了以上問題婚惫,還可以通過以下的方法來分析:
1氛赐、Heap內(nèi)存(老年代)持續(xù)上漲達到設(shè)置的最大內(nèi)存值;
操作方法:jstat -gc <進程ID>先舷,其中<進程ID>是Java進程的ID(使用JVisualVM或JMC也可以查看)
分析:如果Java的堆內(nèi)存(老年代)持續(xù)上漲并達到設(shè)置的最大內(nèi)存值艰管,這通常意味著垃圾回收器(GC)無法有效地回收無用的對象,或者應(yīng)用程序正在創(chuàng)建大量新的對象蒋川,超過了你設(shè)置的堆內(nèi)存大小牲芋。這可能會導(dǎo)致OutOfMemoryError錯誤。
解決方案:
增加堆大修嗲颉:如果你的應(yīng)用程序確實需要更多的內(nèi)存來運行缸浦,你可以嘗試增加堆的大小。但是氮兵,請注意裂逐,這可能會導(dǎo)致系統(tǒng)其他部分的內(nèi)存不足,從而影響性能泣栈。
優(yōu)化代碼:檢查你的代碼卜高,看看是否有不必要的對象創(chuàng)建沼瘫,或者是否有可以重用的對象候址。優(yōu)化你的代碼赏僧,以減少對象的創(chuàng)建和使用夕凝。
優(yōu)化垃圾收集器:根據(jù)你使用的Java版本,你可以嘗試調(diào)整垃圾收集器的參數(shù)鸽照。例如螺捐,你可以嘗試使用G1垃圾收集器或者ZGC,這些垃圾收集器在處理大量內(nèi)存時表現(xiàn)得更好矮燎。
使用Java的內(nèi)存分析工具:例如JVisualVM定血、JMC(Java Mission Control)或者JStack,這些工具可以幫助你找出內(nèi)存中的問題诞外。
使用Java的Profiler:例如VisualVM澜沟、YourKit或者JProfiler,這些工具可以幫助你找出內(nèi)存中的問題峡谊,并給出優(yōu)化的建議茫虽。
2、Full GC 次數(shù)頻繁既们;
操作方法:jconsole <PID> 濒析,在JConsole中,選擇“Java堆”選項卡啥纸,然后選擇“垃圾收集”部分号杏。您將看到有關(guān)Full GC的詳細信息,包括次數(shù)和持續(xù)時間斯棒。如果使用的是云服務(wù)盾致,也可以在云服務(wù)監(jiān)控上查看,例如阿里云荣暮。
分析:Full GC(全局垃圾回收)可能會導(dǎo)致應(yīng)用程序性能下降庭惜,因為Full GC會暫停整個應(yīng)用程序,導(dǎo)致應(yīng)用程序停頓和響應(yīng)延遲穗酥。
解決方案:
系統(tǒng)承載高并發(fā)請求或處理大量數(shù)據(jù)护赊,導(dǎo)致Young GC無法處理,每次Young GC后存活的對象過多迷扇,內(nèi)存分配不合理百揭,Survivor區(qū)過小,導(dǎo)致對象頻繁進入老年代蜓席,頻繁觸發(fā)Full GC。解決方法是合理分配內(nèi)存课锌,調(diào)大SurLo垃圾回收器使用率厨内,調(diào)大Survivor區(qū)祈秕。
系統(tǒng)一次性加載過多數(shù)據(jù)進內(nèi)存,導(dǎo)致頻繁有大對象進入老年帶雏胃,觸發(fā)Full GC请毛。解決方法是分批次加載數(shù)據(jù)。
內(nèi)存泄漏瞭亮,創(chuàng)建大量對象方仿,無法回收,一直占用老年代內(nèi)存统翩,導(dǎo)致頻繁Full GC仙蚜。解決方法是使用MAT工具分析內(nèi)存快照,找出內(nèi)存泄漏的原因并修復(fù)厂汗。
Metaspace(永久代)因加載類過多觸發(fā)Full GC委粉。解決方法是合理設(shè)置類加載器的大小。
誤調(diào)用System.gc()觸發(fā)Full GC娶桦。解決方法是避免濫用System.gc()贾节,因為JVM并不一定會按照這個請求執(zhí)行。
3衷畦、GC 停頓時間過長(超過1秒)栗涂;
操作方法:與查看Full GC次數(shù)相同,VisualVM是一個開源的Java虛擬機監(jiān)視祈争、分析和調(diào)試工具斤程,可以用于監(jiān)控JVM的性能和查看GC的詳細信息。您可以使用VisualVM的“Sampler”和“Profiler”功能來監(jiān)視應(yīng)用程序的性能铛嘱,并查看GC的停頓時長暖释。
分析:如果GC停頓時間過長(超過1秒),這可能會導(dǎo)致應(yīng)用程序性能下降墨吓,因為停頓時間過長會導(dǎo)致應(yīng)用程序的響應(yīng)緩慢和停頓球匕。以下是一些可能導(dǎo)致GC停頓時間過長的原因和解決方法:
解決方案
應(yīng)用程序的內(nèi)存分配不合理,導(dǎo)致GC需要花費更多的時間來回收內(nèi)存帖烘。解決方法是合理分配內(nèi)存亮曹,避免過度使用內(nèi)存。
系統(tǒng)承載高并發(fā)請求或處理大量數(shù)據(jù)秘症,導(dǎo)致內(nèi)存中的對象過多照卦,GC需要花費更多的時間來處理這些對象。解決方法是分批次處理數(shù)據(jù)乡摹,避免一次性加載過多數(shù)據(jù)進內(nèi)存役耕。
內(nèi)存泄漏,創(chuàng)建大量對象聪廉,無法回收瞬痘,一直占用老年代內(nèi)存故慈,導(dǎo)致GC需要花費更多的時間來回收這些對象。解決方法是使用MAT工具分析內(nèi)存快照框全,找出內(nèi)存泄漏的原因并修復(fù)察绷。
GC算法或垃圾收集器選擇不當(dāng),導(dǎo)致GC效率低下津辩。解決方法是根據(jù)應(yīng)用程序的特點選擇適合的GC算法和垃圾收集器拆撼,并進行調(diào)優(yōu)。
代碼中存在大量大對象的創(chuàng)建喘沿,導(dǎo)致GC需要花費更多的時間來處理這些對象闸度。解決方法是避免創(chuàng)建過多的大對象,或者將大對象分解成小對象進行處理摹恨。
本文借鑒:
https://blog.csdn.net/agonie201218/article/details/123748148