JVM調優參數、方法、工具以及案例總結

2021-02-23 JAVA樂園

這種文章挺難寫的,一是JVM參數巨多,二是內容枯燥乏味,但是想理解JVM調優又是沒法避開的環節,本文主要用來總結梳理便於以後翻閱,主要圍繞四個大的方面展開,分別是JVM調優參數、JVM調優方法(流程)、JVM調優工具、JVM調優案例,調優案例目前正在分析,會在將來補上。

垃圾回收有關參數

參數部分,這兒只是做一個總結,更詳細更新的內容請參考Oracle官網:JVM的命令行參數參考

處理器組合參數

關於JVM垃圾處理器區別,參考:JVM調優之垃圾定位、垃圾回收算法、垃圾處理器對比

-XX:+UseSerialGC = Serial New (DefNew) + Serial Old

適用於小型程序。默認情況下不會是這種選項,HotSpot會根據計算及配置和JDK版本自動選擇收集器

-XX:+UseParNewGC = ParNew + SerialOld

這個組合已經很少用(在某些版本中已經廢棄),詳情參考:Why Remove support for ParNew+SerialOld and DefNew+CMS in the future?

-XX:+UseConc(urrent)MarkSweepGC = ParNew + CMS + Serial Old

-XX:+UseParallelGC = Parallel Scavenge + Parallel Old (1.8默認) 【PS + SerialOld】

-XX:+UseParallelOldGC = Parallel Scavenge + Parallel Old

-XX:+UseG1GC = G1

Linux中沒找到默認GC的查看方法,而windows中會列印UseParallelGC

Linux下1.8版本默認的垃圾回收器到底是什麼?

虛擬機參數參數名稱含義默認值解釋說明-Xms初始堆大小物理內存的1/64(<1GB)默認(MinHeapFreeRatio參數可以調整)空餘堆內存小於40%時,JVM就會增大堆直到-Xmx的最大限制.-Xmx最大堆大小物理內存的1/4(<1GB)默認(MaxHeapFreeRatio參數可以調整)空餘堆內存大於70%時,JVM會減少堆直到 -Xms的最小限制-Xmn年輕代大小(1.4or lator)
注意:此處的大小是(eden+ 2 survivor space).與jmap -heap中顯示的New gen是不同的。整個堆大小=年輕代大小 + 年老代大小 + 持久代大小. 增大年輕代後,將會減小年老代大小.此值對系統性能影響較大,Sun官方推薦配置為整個堆的3/8-XX:NewSize設置年輕代大小(for 1.3/1.4)

-XX:MaxNewSize年輕代最大值(for 1.3/1.4)

-XX:PermSize設置持久代(perm gen)初始值物理內存的1/64
-XX:MaxPermSize設置持久代最大值物理內存的1/4
-Xss每個線程的堆棧大小
JDK5.0以後每個線程堆棧大小為1M,以前每個線程堆棧大小為256K.更具應用的線程所需內存大小進行 調整.在相同物理內存下,減小這個值能生成更多的線程.但是作業系統對一個進程內的線程數還是有限制的,不能無限生成,經驗值在3000~5000左右 一般小的應用, 如果棧不是很深, 應該是128k夠用的 大的應用建議使用256k。這個選項對性能影響比較大,需要嚴格的測試。和threadstacksize選項解釋很類似,官方文檔似乎沒有解釋,在論壇中有這樣一句話:"」 -Xss is translated in a VM flag named ThreadStackSize」 一般設置這個值就可以了。-XX:ThreadStackSizeThread Stack Size
(0 means use default stack size) [Sparc: 512; Solaris x86: 320 (was 256 prior in 5.0 and earlier); Sparc 64 bit: 1024; Linux amd64: 1024 (was 0 in 5.0 and earlier); all others 0.]-XX:NewRatio年輕代(包括Eden和兩個Survivor區)與年老代的比值(除去持久代)
-XX:NewRatio=4表示年輕代與年老代所佔比值為1:4,年輕代佔整個堆棧的1/5 Xms=Xmx並且設置了Xmn的情況下,該參數不需要進行設置。-XX:SurvivorRatioEden區與Survivor區的大小比值
設置為8,則兩個Survivor區與一個Eden區的比值為2:8,一個Survivor區佔整個年輕代的1/10-XX:LargePageSizeInBytes內存頁的大小不可設置過大, 會影響Perm的大小
=128m-XX:+UseFastAccessorMethods原始類型的快速優化

-XX:+DisableExplicitGC關閉System.gc()
這個參數需要嚴格的測試-XX:MaxTenuringThreshold垃圾最大年齡
如果設置為0的話,則年輕代對象不經過Survivor區,直接進入年老代. 對於年老代比較多的應用,可以提高效率.如果將此值設置為一個較大值,則年輕代對象會在Survivor區進行多次複製,這樣可以增加對象再年輕代的存活 時間,增加在年輕代即被回收的概率 該參數只有在串行GC時才有效.-XX:+AggressiveOpts加快編譯

-XX:+UseBiasedLocking鎖機制的性能改善

-Xnoclassgc禁用垃圾回收

-XX:SoftRefLRUPolicyMSPerMB每兆堆空閒空間中SoftReference的存活時間1ssoftly reachable objects will remain alive for some amount of time after the last time they were referenced. The default value is one second of lifetime per free megabyte in the heap-XX:PretenureSizeThreshold對象超過多大是直接在舊生代分配0單位字節 新生代採用Parallel Scavenge GC時無效 另一種直接在舊生代分配的情況是大的數組對象,且數組中無外部引用對象.-XX:TLABWasteTargetPercentTLAB佔eden區的百分比1%
-XX:+CollectGen0FirstFullGC時是否先YGCfalse
並行收集器相關參數參數名稱含義默認值解釋說明-XX:+UseParallelGCFull GC採用parallel MSC (此項待驗證)
選擇垃圾收集器為並行收集器.此配置僅對年輕代有效.即上述配置下,年輕代使用並發收集,而年老代仍舊使用串行收集.(此項待驗證)-XX:+UseParNewGC設置年輕代為並行收集
可與CMS收集同時使用 JDK5.0以上,JVM會根據系統配置自行設置,所以無需再設置此值-XX:ParallelGCThreads並行收集器的線程數
此值最好配置與處理器數目相等 同樣適用於CMS-XX:+UseParallelOldGC年老代垃圾收集方式為並行收集(Parallel Compacting)
這個是JAVA 6出現的參數選項-XX:MaxGCPauseMillis每次年輕代垃圾回收的最長時間(最大暫停時間)
如果無法滿足此時間,JVM會自動調整年輕代大小,以滿足此值.-XX:+UseAdaptiveSizePolicy自動選擇年輕代區大小和相應的Survivor區比例
設置此選項後,並行收集器會自動選擇年輕代區大小和相應的Survivor區比例,以達到目標系統規定的最低相應時間或者收集頻率等,此值建議使用並行收集器時,一直打開.-XX:GCTimeRatio設置垃圾回收時間佔程序運行時間的百分比
公式為1/(1+n)-XX:+ScavengeBeforeFullGCFull GC前調用YGCtrueDo young generation GC prior to a full GC. (Introduced in 1.4.1.)CMS處理器參數設置參數名稱含義默認值解釋說明-XX:+UseConcMarkSweepGC使用CMS內存收集
測試中配置這個以後,-XX:NewRatio=4的配置失效了,原因不明.所以,此時年輕代大小最好用-Xmn設置.???-XX:+AggressiveHeap

試圖是使用大量的物理內存 長時間大內存使用的優化,能檢查計算資源(內存, 處理器數量) 至少需要256MB內存 大量的CPU/內存, (在1.4.1在4CPU的機器上已經顯示有提升)-XX:CMSFullGCsBeforeCompaction多少次後進行內存壓縮
由於並發收集器不對內存空間進行壓縮,整理,所以運行一段時間以後會產生"碎片",使得運行效率降低.此值設置運行多少次GC以後對內存空間進行壓縮,整理.-XX:+CMSParallelRemarkEnabled降低標記停頓

-XX+UseCMSCompactAtFullCollection在FULL GC的時候, 對年老代的壓縮
CMS是不會移動內存的, 因此, 這個非常容易產生碎片, 導致內存不夠用, 因此, 內存的壓縮這個時候就會被啟用。增加這個參數是個好習慣。可能會影響性能,但是可以消除碎片-XX:+UseCMSInitiatingOccupancyOnly使用手動定義初始化定義開始CMS收集
禁止hostspot自行觸發CMS GC-XX:CMSInitiatingOccupancyFraction=70使用cms作為垃圾回收 使用70%後開始CMS收集92為了保證不出現promotion failed(見下面介紹)錯誤,該值的設置需要滿足以下公式CMSInitiatingOccupancyFraction計算公式-XX:CMSInitiatingPermOccupancyFraction設置Perm Gen使用到達多少比率時觸發92
-XX:+CMSIncrementalMode設置為增量模式
用於單CPU情況-XX:+CMSClassUnloadingEnabled


JVM輔助信息參數設置參數名稱含義默認值解釋說明-XX:+PrintGC

輸出形式:[GC 118250K->113543K(130112K), 0.0094143 secs] [Full GC 121376K->10414K(130112K), 0.0650971 secs]-XX:+PrintGCDetails

輸出形式:[GC [DefNew: 8614K->781K(9088K), 0.0123035 secs] 118250K->113543K(130112K), 0.0124633 secs] [GC [DefNew: 8614K->8614K(9088K), 0.0000665 secs][Tenured: 112761K->10414K(121024K), 0.0433488 secs] 121376K->10414K(130112K), 0.0436268 secs]-XX:+PrintGCTimeStamps


-XX:+PrintGC:PrintGCTimeStamps

可與-XX:+PrintGC -XX:+PrintGCDetails混合使用 輸出形式:11.851: [GC 98328K->93620K(130112K), 0.0082960 secs]-XX:+PrintGCApplicationStoppedTime列印垃圾回收期間程序暫停的時間.可與上面混合使用
輸出形式:Total time for which application threads were stopped: 0.0468229 seconds-XX:+PrintGCApplicationConcurrentTime列印每次垃圾回收前,程序未中斷的執行時間.可與上面混合使用
輸出形式:Application time: 0.5291524 seconds-XX:+PrintHeapAtGC列印GC前後的詳細堆棧信息

-Xloggc:filename把相關日誌信息記錄到文件以便分析. 與上面幾個配合使用

-XX:+PrintClassHistogramgarbage collects before printing the histogram.

-XX:+PrintTLAB查看TLAB空間的使用情況

XX:+PrintTenuringDistribution查看每次minor GC後新的存活周期的閾值
Desired survivor size 1048576 bytes, new threshold 7 (max 15) new threshold 7即標識新的存活周期的閾值為7。JVM GC垃圾回收器參數設置

JVM給出了3種選擇:串行收集器並行收集器並發收集器。串行收集器只適用於小數據量的情況,所以生產環境的選擇主要是並行收集器和並發收集器。默認情況下JDK5.0以前都是使用串行收集器,如果想使用其他收集器需要在啟動時加入相應參數。JDK5.0以後,JVM會根據當前系統配置進行智能判斷。

串行收集器
-XX:+UseSerialGC:設置串行收集器。

並行收集器(吞吐量優先)
-XX:+UseParallelGC:設置為並行收集器。此配置僅對年輕代有效。即年輕代使用並行收集,而年老代仍使用串行收集。

-XX:ParallelGCThreads=20:配置並行收集器的線程數,即:同時有多少個線程一起進行垃圾回收。此值建議配置與CPU數目相等。

-XX:+UseParallelOldGC:配置年老代垃圾收集方式為並行收集。JDK6.0開始支持對年老代並行收集。

-XX:MaxGCPauseMillis=100:設置每次年輕代垃圾回收的最長時間(單位毫秒)。如果無法滿足此時間,JVM會自動調整年輕代大小,以滿足此時間。

-XX:+UseAdaptiveSizePolicy:設置此選項後,並行收集器會自動調整年輕代Eden區大小和Survivor區大小的比例,以達成目標系統規定的最低響應時間或者收集頻率等指標。此參數建議在使用並行收集器時,一直打開。
並發收集器(響應時間優先)

並行收集器

-XX:+UseConcMarkSweepGC:即CMS收集,設置年老代為並發收集。CMS收集是JDK1.4後期版本開始引入的新GC算法。它的主要適合場景是對響應時間的重要性需求大於對吞吐量的需求,能夠承受垃圾回收線程和應用線程共享CPU資源,並且應用中存在比較多的長生命周期對象。CMS收集的目標是儘量減少應用的暫停時間,減少Full GC發生的機率,利用和應用程式線程並發的垃圾回收線程來標記清除年老代內存。

-XX:+UseParNewGC:設置年輕代為並發收集。可與CMS收集同時使用。JDK5.0以上,JVM會根據系統配置自行設置,所以無需再設置此參數。

-XX:CMSFullGCsBeforeCompaction=0:由於並發收集器不對內存空間進行壓縮和整理,所以運行一段時間並行收集以後會產生內存碎片,內存使用效率降低。此參數設置運行0次Full GC後對內存空間進行壓縮和整理,即每次Full GC後立刻開始壓縮和整理內存。

-XX:+UseCMSCompactAtFullCollection:打開內存空間的壓縮和整理,在Full GC後執行。可能會影響性能,但可以消除內存碎片。

-XX:+CMSIncrementalMode:設置為增量收集模式。一般適用於單CPU情況。

-XX:CMSInitiatingOccupancyFraction=70:表示年老代內存空間使用到70%時就開始執行CMS收集,以確保年老代有足夠的空間接納來自年輕代的對象,避免Full GC的發生。

其它垃圾回收參數

-XX:+ScavengeBeforeFullGC:年輕代GC優於Full GC執行。

-XX:-DisableExplicitGC:不響應 System.gc() 代碼。

-XX:+UseThreadPriorities:啟用本地線程優先級API。即使 java.lang.Thread.setPriority() 生效,不啟用則無效。

-XX:SoftRefLRUPolicyMSPerMB=0:軟引用對象在最後一次被訪問後能存活0毫秒(JVM默認為1000毫秒)。

-XX:TargetSurvivorRatio=90:允許90%的Survivor區被佔用(JVM默認為50%)。提高對於Survivor區的使用率。

JVM參數優先級

-Xmn,-XX:NewSize/-XX:MaxNewSize,-XX:NewRatio 3組參數都可以影響年輕代的大小,混合使用的情況下,優先級是什麼?

答案如下:

高優先級:-XX:NewSize/-XX:MaxNewSize
中優先級:-Xmn(默認等效 -Xmn=-XX:NewSize=-XX:MaxNewSize=?)
低優先級:-XX:NewRatio

推薦使用-Xmn參數,原因是這個參數簡潔,相當於一次設定 NewSize/MaxNewSIze,而且兩者相等,適用於生產環境。-Xmn 配合 -Xms/-Xmx,即可將堆內存布局完成。

-Xmn參數是在JDK 1.4 開始支持。

下面用一些小案例加深理解:

HelloGC是java代碼編譯後的一個class文件,代碼:

public class T01_HelloGC {
public static void main(String[] args) {

for(int i=0; i<10000; i++) {
byte[] b = new byte[1024 * 1024];
}
}
}

java -XX:+PrintCommandLineFlags HelloGC

[root@localhost courage]# java -XX:+PrintCommandLineFlags T01_HelloGC
-XX:InitialHeapSize=61780800 -XX:MaxHeapSize=988492800 -XX:+PrintCommandLineFlags -XX
:+UseCompressedClassPointers -XX:+UseCompressedOops -XX:+UseParallelGC

java -Xmn10M -Xms40M -Xmx60M -XX:+PrintCommandLineFlags -XX:+PrintGC HelloGC
PrintGCDetails PrintGCTimeStamps PrintGCCauses

結果:

-XX:InitialHeapSize=41943040 -XX:MaxHeapSize=62914560 -XX:MaxNewSize=10485760 -XX:NewSize=10485760 -XX:+PrintCommandLineFlags -XX:+PrintGC -XX:+UseCompressedClassPointers -XX:+UseCompressedOops
-XX:+UseParallelGC[GC (Allocation Failure) 7839K->392K(39936K), 0.0015452 secs]
[GC (Allocation Failure) 7720K->336K(39936K), 0.0005439 secs]
[GC (Allocation Failure) 7656K->336K(39936K), 0.0005749 secs]
[GC (Allocation Failure) 7659K->368K(39936K), 0.0005095 secs]
[GC (Allocation Failure) 7693K->336K(39936K), 0.0004385 secs]
[GC (Allocation Failure) 7662K->304K(40448K), 0.0028468 secs]
.

命令解釋:

java:表示使用java執行器執行
-Xmn10M :表示設置年輕代值為10M
-Xms40M :表示設置堆內存的最小Heap值為40M
-Xmx60M :表示設置堆內存的最大Heap值為60M
-XX:+PrintCommandLineFlags:列印顯式隱式參數,就是結果前三行
-XX:+PrintGC : 列印垃圾回收有關信息
HelloGC :這是需要執行的啟動類
PrintGCDetails :列印GC詳細信息
PrintGCTimeStamps :列印GC時間戳
PrintGCCauses :列印GC產生的原因

結果解釋:

java -XX:+UseConcMarkSweepGC -XX:+PrintCommandLineFlags HelloGC

表示使用CMS垃圾收集器,同時列印參數
列印結果:

-XX:InitialHeapSize=61780800
-XX:MaxHeapSize=988492800
-XX:MaxNewSize=329252864
-XX:MaxTenuringThreshold=6
-XX:OldPLABSize=16
-XX:+PrintCommandLineFlags
-XX:+UseCompressedClassPointers
-XX:+UseCompressedOops
-XX:+UseConcMarkSweepGC
-XX:+UseParNewGC

java -XX:+PrintFlagsInitial 默認參數值

java -XX:+PrintFlagsFinal 最終參數值

java -XX:+PrintFlagsFinal | grep xxx 找到對應的參數

java -XX:+PrintFlagsFinal -version |grep GC

JVM調優流程

JVM調優,設計到三個大的方面,在伺服器出現問題之前要先根據業務場景選擇合適的垃圾處理器,設置不同的虛擬機參數,運行中觀察GC日誌,分析性能,分析問題定位問題,虛擬機排錯等內容,如果伺服器掛掉了,要及時生成日誌文件便於找到問題所在。

調優前的基礎概念

目前的垃圾處理器中,一類是以吞吐量優先,一類是以響應時間優先:

吞吐量=用戶代碼執行時間用戶代碼執行時間+垃圾回收執行時間吞吐量=用戶代碼執行時間用戶代碼執行時間+垃圾回收執行時間

響應時間:STW越短,響應時間越好

對吞吐量、響應時間、QPS、並發數相關概念可以參考:吞吐量(TPS)、QPS、並發數、響應時間(RT)概念

所謂調優,首先確定追求什麼,是吞吐量? 還是追求響應時間?還是在滿足一定的響應時間的情況下,要求達到多大的吞吐量,等等。一般情況下追求吞吐量的有以下領域:科學計算、數據挖掘等。吞吐量優先的垃圾處理器組合一般為:Parallel Scavenge + Parallel Old (PS + PO)。

而追求響應時間的業務有:網站相關 (JDK 1.8之後 G1,之前可以ParNew + CMS + Serial Old)

什麼是調優?

根據需求進行JVM規劃和預調優

優化運行JVM運行環境(慢,卡頓)

解決JVM運行過程中出現的各種問題(OOM)

調優之前的規劃

-Xloggc:/opt/xxx/logs/xxx-xxx-gc-%t.log
-XX:+UseGCLogFileRotation
-XX:NumberOfGCLogFiles=5
-XX:GCLogFileSize=20M
-XX:+PrintGCDetails
-XX:+PrintGCDateStamps
-XX:+PrintGCCause

日誌參數解釋說明:

/opt/xxx/logs/xxx-xxx-gc-%t.log 中XXX表示路徑,%t表示時間戳,意思是給日誌文件添加一個時間標記,如果不添加的話,也就意味著每次虛擬機啟動都會使用原來的日誌名,那麼會被重寫。

Rotation中文意思是循環、輪流,意味著這個GC日誌會循環寫

GCLogFileSize=20M 指定一個日誌大小為20M,太大了不利於分析,太小又會產生過多的日誌文件

NumberOfGCLogFiles=5 : 指定生成的日誌數目

PrintGCDateStamps :PrintGCDateStamps會列印具體的時間,而PrintGCTimeStamps

主要列印針對JVM啟動的時候的相對時間,相對來說前者更消耗內存。

或者每天產生一個日誌文件

響應時間、停頓時間 [CMS G1 ZGC] (需要給用戶作響應)

吞吐量 = 用戶時間 /( 用戶時間 + GC時間) [PS+PO]

熟悉業務場景(沒有最好的垃圾回收器,只有最合適的垃圾回收器)

選擇回收器組合

計算內存需求(經驗值 1.5G 16G)

選定CPU(越高越好)

設定年代大小、升級年齡

設定日誌參數

觀察日誌情況
日誌有分析工具,可視化分析工具有GCeasy和GCViewer。

CPU高負荷排查流程

系統CPU經常100%,如何調優?(面試高頻) CPU100%那麼一定有線程在佔用系統資源,

找出哪個進程cpu高(top)

該進程中的哪個線程cpu高(top -Hp)

導出該線程的堆棧 (jstack)

查找哪個方法(棧幀)消耗時間 (jstack)

工作線程佔比高 | 垃圾回收線程佔比高

系統內存飆高,如何查找問題?(面試高頻)

導出堆內存 (jmap)

分析 (jhat jvisualvm mat jprofiler ... )

如何監控JVM

jstat jvisualvm jprofiler arthas top...

CPU高負荷排查案例

測試代碼:

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

/**
* 從資料庫中讀取信用數據,套用模型,並把結果進行記錄和傳輸
*/

public class T15_FullGC_Problem01 {

private static class CardInfo {
BigDecimal price = new BigDecimal(0.0);
String name = "張三";
int age = 5;
Date birthdate = new Date();

public void m() {}
}

private static ScheduledThreadPoolExecutor executor = new ScheduledThreadPoolExecutor(50,
new ThreadPoolExecutor.DiscardOldestPolicy());

public static void main(String[] args) throws Exception {
executor.setMaximumPoolSize(50);

for (;;){
modelFit();
Thread.sleep(100);
}
}

private static void modelFit(){
List<CardInfo> taskList = getAllCardInfo();
taskList.forEach(info -> {
// do something
executor.scheduleWithFixedDelay(() -> {
//do sth with info
info.m();

}, 2, 3, TimeUnit.SECONDS);
});
}

private static List<CardInfo> getAllCardInfo(){
List<CardInfo> taskList = new ArrayList<>();

for (int i = 0; i < 100; i++) {
CardInfo ci = new CardInfo();
taskList.add(ci);
}

return taskList;
}
}

java -Xms200M -Xmx200M -XX:+PrintGC com.courage.jvm.gc.T15_FullGC_Problem01

收到CPU報警信息(CPU Memory)

top命令觀察到問題:內存不斷增長 CPU佔用率居高不下

[root@localhost ~]# top
top - 22:03:18 up 40 min, 5 users, load average: 0.09, 0.16, 0.34
Tasks: 210 total, 1 running, 209 sleeping, 0 stopped, 0 zombie
%Cpu(s): 0.2 us, 3.0 sy, 0.0 ni, 96.8 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
KiB Mem : 3861300 total, 2355260 free, 904588 used, 601452 buff/cache
KiB Swap: 4063228 total, 4063228 free, 0 used. 2716336 avail Mem

PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
3751 root 20 0 3780976 93864 11816 S 42.2 2.4 0:21.00 java
1868 mysql 20 0 1907600 357452 14744 S 0.7 9.3 0:17.40 mysqld
3816 root 20 0 162124 2352 1580 R 0.3 0.1 0:00.12 top

top -Hp 觀察進程中的線程,哪個線程CPU和內存佔比高

[root@localhost ~]# top -Hp 3751
top - 22:03:15 up 40 min, 5 users, load average: 0.09, 0.16, 0.34
Threads: 66 total, 0 running, 66 sleeping, 0 stopped, 0 zombie
%Cpu(s): 0.0 us, 2.5 sy, 0.0 ni, 97.5 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
KiB Mem : 3861300 total, 2354800 free, 905048 used, 601452 buff/cache
KiB Swap: 4063228 total, 4063228 free, 0 used. 2715876 avail Mem

PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
3801 root 20 0 3780976 93864 11816 S 1.3 2.4 0:00.40 java
3766 root 20 0 3780976 93864 11816 S 1.0 2.4 0:00.37 java
3768 root 20 0 3780976 93864 11816 S 1.0 2.4 0:00.36 java
3770 root 20 0 3780976 93864 11816 S 1.0 2.4 0:00.39 java

jps定位具體java進程,jstack 定位線程狀況

[root@localhost ~]# jstack 3751
2021-02-07 22:03:03
Full thread dump Java HotSpot(TM) 64-Bit Server VM (25.271-b09 mixed mode):

"Attach Listener" #59 daemon prio=9 os_prio=0 tid=0x00007f66bc002800 nid=0xf10 waiting on condition [0x0000000000000000]
java.lang.Thread.State: RUNNABLE

"pool-1-thread-50" #58 prio=5 os_prio=0 tid=0x00007f66fc1de800 nid=0xee7 waiting on condition [0x00007f66e4ecd000]
java.lang.Thread.State: WAITING (parking)
at sun.misc.Unsafe.park(Native Method)
- parking to wait for <0x00000000ff0083a0> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject)
.

需要注意的是,jstack與top -Hp Port導出的棧埠號存在十六進位轉換關係,例如jstack導出的" nid=0xf10 "對應"3801"。
對於上面列印的信息,重點關注跟Waiting有關的,看看在等待什麼,例如:

WAITING BLOCKED eg. waiting on <0x0000000088ca3310> (a java.lang.Object)

假如有一個進程中100個線程,很多線程都在waiting on ,一定要找到是哪個線程持有這把鎖,怎麼找?搜索jstack dump的信息,看哪個線程持有這把鎖RUNNABLE。

如果僅僅是看JAVA線程,可以使用jps命令重點關注:

[root@localhost ~]# jps
4818 Jps
4746 T15_FullGC_Problem01

為什麼阿里規範裡規定,線程的名稱(尤其是線程池)都要寫有意義的名稱 怎麼樣自定義線程池裡的線程名稱?(自定義ThreadFactory)

jinfo pid 進程詳細信息

[root@localhost ~]# jinfo 6741
Attaching to process ID 6741, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 25.271-b09
Java System Properties:

java.runtime.name = Java(TM) SE Runtime Environment
java.vm.version = 25.271-b09
sun.boot.library.path = /usr/local/java/jdk1.8.0_271/jre/lib/amd64
java.vendor.url = http://java.oracle.com/
java.vm.vendor = Oracle Corporation
path.separator = :
file.encoding.pkg = sun.io
java.vm.name = Java HotSpot(TM) 64-Bit Server VM
sun.os.patch.level = unknown
sun.java.launcher = SUN_STANDARD
user.country = CN
user.dir = /usr/courage/gc/com/courage
java.vm.specification.name = Java Virtual Machine Specification
java.runtime.version = 1.8.0_271-b09
java.awt.graphicsenv = sun.awt.X11GraphicsEnvironment
os.arch = amd64
java.endorsed.dirs = /usr/local/java/jdk1.8.0_271/jre/lib/endorsed
java.io.tmpdir = /tmp
line.separator =

java.vm.specification.vendor = Oracle Corporation
os.name = Linux
sun.jnu.encoding = UTF-8
java.library.path = /usr/java/packages/lib/amd64:/usr/lib64:/lib64:/lib:/usr/
libjava.specification.name = Java Platform API Specification
java.class.version = 52.0
sun.management.compiler = HotSpot 64-Bit Tiered Compilers
os.version = 3.10.0-1127.el7.x86_64
user.home = /root
user.timezone =
java.awt.printerjob = sun.print.PSPrinterJob
file.encoding = UTF-8
java.specification.version = 1.8
user.name = root
java.class.path = .
java.vm.specification.version = 1.8
sun.arch.data.model = 64
sun.java.command = T15_FullGC_Problem01
java.home = /usr/local/java/jdk1.8.0_271/jre
user.language = zh
java.specification.vendor = Oracle Corporation
awt.toolkit = sun.awt.X11.XToolkit
java.vm.info = mixed mode
java.version = 1.8.0_271
java.ext.dirs = /usr/local/java/jdk1.8.0_271/jre/lib/ext:/usr/java/packages/l
ib/extsun.boot.class.path = /usr/local/java/jdk1.8.0_271/jre/lib/resources.jar:/usr
/local/java/jdk1.8.0_271/jre/lib/rt.jar:/usr/local/java/jdk1.8.0_271/jre/lib/sunrsasign.jar:/usr/local/java/jdk1.8.0_271/jre/lib/jsse.jar:/usr/local/java/jdk1.8.0_271/jre/lib/jce.jar:/usr/local/java/jdk1.8.0_271/jre/lib/charsets.jar:/usr/local/java/jdk1.8.0_271/jre/lib/jfr.jar:/usr/local/java/jdk1.8.0_271/jre/classesjava.vendor = Oracle Corporation
file.separator = /
java.vendor.url.bug = http://bugreport.sun.com/bugreport/
sun.io.unicode.encoding = UnicodeLittle
sun.cpu.endian = little
sun.cpu.isalist =

VM Flags:
Non-default VM flags: -XX:CICompilerCount=3 -XX:InitialHeapSize=209715200 -XX
:MaxHeapSize=209715200 -XX:MaxNewSize=69730304 -XX:MinHeapDeltaBytes=524288 -XX:NewSize=69730304 -XX:OldSize=139984896 -XX:+PrintGC -XX:+UseCompressedClassPointers -XX:+UseCompressedOops -XX:+UseFastUnorderedTimeStamps -XX:+UseParallelGC Command line: -Xms200M -Xmx200M -XX:+PrintGC

jstat -gc 動態觀察gc情況 / 閱讀GC日誌發現頻繁GC / arthas觀察 / jconsole/jvisualVM/ Jprofiler(最好用)

jstat gc 4655 500 : 每500毫秒列印埠4655的GC的情況

S0C:第一個倖存區的大小

S1C:第二個倖存區的大小

S0U:第一個倖存區的使用大小

S1U:第二個倖存區的使用大小

EC:伊甸園區的大小

EU:伊甸園區的使用大小

OC:老年代大小

OU:老年代使用大小

MC:方法區大小

MU:方法區使用大小

CCSC:壓縮類空間大小

CCSU:壓縮類空間使用大小

YGC:年輕代垃圾回收次數

YGCT:年輕代垃圾回收消耗時間

FGC:老年代垃圾回收次數

FGCT:老年代垃圾回收消耗時間

GCT:垃圾回收消耗總時間

如果面試官問你是怎麼定位OOM問題的?能否用圖形界面(不能!因為圖形界面會影響伺服器性能)
1:已經上線的系統不用圖形界面用什麼?(cmdline arthas)
2:圖形界面到底用在什麼地方?測試!測試的時候進行監控!(壓測觀察)

jmap -histo 6892 | head -10,查找有多少對象產生

這明顯能看出來是1對應的類創造的實例instances太多了,反過來追蹤代碼

jmap -dump:format=b,file=xxx pid :

線上系統,內存特別大,jmap執行期間會對進程產生很大影響,甚至卡頓(電商不適合)
1:設定了參數HeapDump,OOM的時候會自動產生堆轉儲文件
2:很多伺服器備份(高可用),停掉這臺伺服器對其他伺服器不影響
3:在線定位(一般小點兒公司用不到)

[root@localhost ~]# jmap -dump:format=b,file=2021_2_8.dump 6892
Dumping heap to /root/2021_2_8.dump ...
Heap dump file created

dump文件存放位置:

java -Xms20M -Xmx20M -XX:+UseParallelGC -XX:+HeapDumpOnOutOfMemoryError com.courage.jvm.gc.T15_FullGC_Problem01
上面的意思是當發生內存溢出時自動生成堆轉儲文件,需要注意的是,如果生成了這個文件先不要重啟伺服器,將這個文件保存好之後再重啟。

使用MAT / jhat /jvisualvm 進行dump文件分析

[root@localhost ~]# jhat -J-Xmx512M 2021_2_8.dump

報錯:


原因是設置的堆最大值太小了,將512M設置成1024M重新啟動即可:

```shell
[root@localhost ~]# jhat -J-Xmx1024M 2021_2_8.dump
Reading from 2021_2_8.dump...
Dump file created Mon Feb 08 09:00:56 CST 2021
Snapshot read, resolving...
Resolving 4609885 objects...
Chasing references, expect 921 dots...
....Eliminating duplicate references.
.Snapshot resolved.
Started HTTP server on port 7000
Server is ready.
```


瀏覽器輸入請求http://192.168.182.130:7000 即可查看,拉到最後:找到對應連結 可以使用OQL查找特定問題對象


其他可以參考:白灰——軟體測試

最後找到代碼的問題

JVM調優工具jconsole遠程連接

程序啟動加入參數:

java -Djava.rmi.server.hostname=192.168.182.130
-Dcom.sun.management.jmxremote
-Dcom.sun.management.jmxremote.port=11111
-Dcom.sun.management.jmxremote.authenticate=false
-Dcom.sun.management.jmxremote.ssl=false XXX

如果遭遇 Local host name unknown:XXX的錯誤,修改/etc/hosts文件,把XXX加入進去

192.168.182.130 basic localhost localhost.localdomain localhost4 localhost4.localdomain4
::1 localhost localhost.localdomain localhost6 localhost6.localdomain6

關閉linux防火牆(實戰中應該打開對應埠)

service iptables stop
chkconfig iptables off #永久關閉

windows上打開 jconsole遠程連接 192.168.182.130:11111

jvisualvm遠程連接

這個軟體在JDK8以後版本中移除了,使用的話需要額外下載,並且要在etc/visualvm.conf中修改默認的JDK_Home地址。
參考:使用jvisualvm的jstatd方式遠程監控Java程序

阿里巴巴Arthas

這個直接看官網就行了,純中文:Arthas 用戶文檔

JVM調優案例參數設置之承受海量訪問的動態Web應用

伺服器配置:8 核 CPU, 8G MEM, JDK 1.6.X

參數方案:
-server -Xmx3550m -Xms3550m -Xmn1256m -Xss128k -XX:SurvivorRatio=6 -XX:MaxPermSize=256m -XX:ParallelGCThreads=8 -XX:MaxTenuringThreshold=0 -XX:+UseConcMarkSweepGC

調優說明:
-Xmx 與 -Xms 相同以避免JVM反覆重新申請內存。-Xmx 的大小約等於系統內存大小的一半,即充分利用系統資源,又給予系統安全運行的空間。
-Xmn1256m 設置年輕代大小為1256MB。此值對系統性能影響較大,Sun官方推薦配置年輕代大小為整個堆的3/8。
-Xss128k 設置較小的線程棧以支持創建更多的線程,支持海量訪問,並提升系統性能。
-XX:SurvivorRatio=6 設置年輕代中Eden區與Survivor區的比值。系統默認是8,根據經驗設置為6,則2個Survivor區與1個Eden區的比值為2:6,一個Survivor區佔整個年輕代的1/8。
-XX:ParallelGCThreads=8 配置並行收集器的線程數,即同時8個線程一起進行垃圾回收。此值一般配置為與CPU數目相等。
-XX:MaxTenuringThreshold=0 設置垃圾最大年齡(在年輕代的存活次數)。如果設置為0的話,則年輕代對象不經過Survivor區直接進入年老代。對於年老代比較多的應用,可以提高效率;如果將此值設置為一個較大值,則年輕代對象會在Survivor區進行多次複製,這樣可以增加對象再年輕代的存活時間,增加在年輕代即被回收的概率。根據被海量訪問的動態Web應用之特點,其內存要麼被緩存起來以減少直接訪問DB,要麼被快速回收以支持高並發海量請求,因此其內存對象在年輕代存活多次意義不大,可以直接進入年老代,根據實際應用效果,在這裡設置此值為0。
-XX:+UseConcMarkSweepGC 設置年老代為並發收集。CMS(ConcMarkSweepGC)收集的目標是儘量減少應用的暫停時間,減少Full GC發生的機率,利用和應用程式線程並發的垃圾回收線程來標記清除年老代內存,適用於應用中存在比較多的長生命周期對象的情況。

參數設置之內部集成構建伺服器

高性能數據處理的工具應用
伺服器配置:1 核 CPU, 4G MEM, JDK 1.6.X
參數方案:
-server -XX:PermSize=196m -XX:MaxPermSize=196m -Xmn320m -Xms768m -Xmx1024m
調優說明:
-XX:PermSize=196m -XX:MaxPermSize=196m 根據集成構建的特點,大規模的系統編譯可能需要加載大量的Java類到內存中,所以預先分配好大量的持久代內存是高效和必要的。
-Xmn320m 遵循年輕代大小為整個堆的3/8原則。
-Xms768m -Xmx1024m 根據系統大致能夠承受的堆內存大小設置即可

source: https://www.cnblogs.com/Courage129/p/14387908.html

相關焦點

  • java垃圾回收以及jvm參數調優概述
    本文從基礎的理論入手,結合作者在實際項目中的實際使用來闡述JVM的垃圾回收機制以及如何對JVM進行調優。建議可以保存下來慢慢看,或者找一段安靜的時間全片通讀。關於作者,想必solr中國的小夥伴都不會陌生,這這裡就不做過多的介紹了。本文共分為五個部分,詳細闡述JAVA的垃圾回收以及JVM的參數調優。
  • JVM性能調優實踐
    這裡有幾個比較重要的指標:當然,和CAP原則一樣,同時滿足一個程序內存佔用小、延遲低、高吞吐量是不可能的,程序的目標不同,調優時所考慮的方向也不同,在調優之前,必須要結合實際場景,有明確的的優化目標,找到性能瓶頸,對瓶頸有針對性的優化,最後進行測試,通過各種監控工具確認調優後的結果是否符合目標。
  • JVM超神之路:年後跳槽需要的JVM知識點,周末給你整理了一份!!!
    13、棧上分配是什麼意思14、簡述下對象的分配規則四、實戰調優1、你在項目中都使用了哪些參數列印GC?2、常用的調優工具有哪些?>G1常用參數四、實戰調優1、你在項目中都使用了哪些參數列印GC?JDK內置的命令行:jps(查看jvm進程信息)、jstat(監視jvm運行狀態的,比如gc情況、jvm內存情況、類加載情況等)、jinfo(查看jvm參數的,也可動態調整)、jmap(生成dump文件的,在dump的時候會影響線上服務)、jhat(分析dump的,但是一般都將dump導出放到mat上分析)、jstack(查看線程的)。
  • JVM、GC 大串講,面試夠用了
    JRE (Java 運行時環境),包含了jvm和core lib。JDK (Java 開發工具包),它集成了jre和一些工具。比如javac.exe,java.exe,jar.exe等。大家都知道,要想執行java程序,需要安裝jdk。JVM 初識JVM其實是一種規範,它提供可以執行Java字節碼的運行時環境。不同的供應商提供這種規範的不同實現。
  • 5款強大的JVM 性能調優監控工具 !
    本文將對一些常用的JVM性能調優監控工具進行介紹,希望能起拋磚引玉之用。一、 jps(Java Virtual Machine Process Status Tool)      :基礎工具   jps主要用來輸出JVM中運行的進程狀態信息。語法格式如下:如果不指定hostid就默認為當前主機或伺服器。
  • JVM源碼分析之jstat工具原理完全解讀
    UsePerfData,那這個文件是不會存在的,默認情況下PerfDisableSharedMem是關閉的,UsePerfData是打開的,所以默認情況下PerfData文件是存在的。對對於UsePerfData和PerfDisableSharedMem這兩個參數,這裡著重講一下:UsePerfData:如果關閉了UsePerfData這個參數,那麼jvm啟動過程中perf memory都不會被創建,jvm運行過程中自然不會再將這些性能數據保存起來,默認情況是是打開的PerfDisableSharedMem:該參數決定了存儲PerfData的內存是不是可以被共享
  • JVM之jvisualvm的簡單使用
    本教程使用的JDK版本為:java.version=1.6.0_45;java.vm.name=Java HotSpot(TM) 64-Bit Server VMjvisualvm連接本地JVM進程先在本地main方法裡面寫一個死循環並且啟動起來。
  • jvm面試系列一:java內存模型你掌握了多少?
    今天我們就來聊一聊Java內存模型,面試中面試官會通過考察你對jvm的理解更深入得了解你的水平。
  • 8款JVM性能調優監控工具(提高開發效率)
    OK,我們就一個一個去分析一下這些工具是幹嘛的,以及如何去使用的。二、工具1、jps:虛擬機進程狀況工具jps主要用來輸出JVM中運行的進程狀態信息。語法格式如下:jps [options] [hostid]第一個參數:options-q 不輸出類名、Jar名和傳入main方法的參數-m 輸出傳入main方法的參數-l 輸出main類或Jar的全限名-v 輸出傳入JVM的參數第二個參數:hostid
  • JVM工具之jstat總結
    jstatjstat命令工具是排除JVMGC問題不可或缺的工具
  • Java:故障排查、JVM性能監控工具單
    使用分析工具能提升咱們分析數據以及定位並解決問題的效率。常用命令JDK提供了一系列的用於監控、診斷Java進程的工具,它們在JDK安裝目錄``bin``目錄下,我們該如何使用它去得到有用的信息並分析系統問題以及性能瓶頸呢?下面詳細介紹。
  • JAVA JVM調優實戰:G1中的to-space exhausted問題
    針對上面的問題,我們最終確定了下面的調優建議:這次沒有發生FGC,可能是由於我前面將xmx和xms調大了導致的,這次準備將xmx和xms先調回到原來的值;加上HeapDumpAfterFullGC參數,下次再發生類似情況的時候,就會觸發FGC,然後自動
  • 大型企業JVM性能調優實戰Java垃圾收集器及gcroot
    02:JVM類加載機制大型企業JVM性能調優實戰之gcroot01:JVM類加載機制 - 類加載的生命周期概述java的class類文件實際上是二進位(字節碼)文件格式,class文件中包含了java虛擬機指令集和符號表以及若干其他輔助信息。
  • Python 環境下的自動化機器學習超參數調優
    機器學習算法的性能高度依賴於超參數的選擇,對機器學習超參數進行調優是一項繁瑣但卻至關重要的任務。本文介紹了一個使用「Hyperopt」庫對梯度提升機(GBM)進行貝葉斯超參數調優的完整示例,並著重介紹了其實現過程。由於機器學習算法的性能高度依賴於超參數的選擇,對機器學習超參數進行調優是一項繁瑣但至關重要的任務。
  • 一文詳解超參數調優方法
    (hyperparameter)的調優方法。超參數,在訓練中一般是固定數值或者以預設規則變化,比如批大小(batch size)、學習率(learning rate)、正則化項係數(weight decay)、核函數中的 gamma 等。超參數調優的目標通常是最小化泛化誤差(generalization error),也可以根據具體任務自定義其他優化目標。
  • 「JVM教程與調優」了解JVM 堆內存溢出以及非堆內存溢出
    訪問測試啟動時候設置內存參數。記得選中我們的Arguments,在JVM 參數中,將我們的值設置進去。最後點擊Run運行起來。注意:這裡我們測試的時候可以很簡單的看出在哪裡出現的問題,但是在實際生產環境中並沒有那麼簡單,因此我們需要藉助工具,來定位這些問題。後續我們來介紹一下。
  • JVM 面試題解答(40道全)
    摘自:https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-2.html#jvms-2.56、運行時棧幀包含哪些結構?JVM 試圖定義一種統一的內存模型,能將各種底層硬體以及作業系統的內存訪問差異進行封裝,使 Java 程序在不同硬體以及作業系統上都能達到相同的並發效果。它分為工作內存和主內存,線程無法對主存儲器直接進行操作,如果一個線程要和另外一個線程通信,那麼只能通過主存進行交換。8、JVM 如何確定垃圾對象?
  • JVM菜鳥進階高手之路十四:分析篇
    題目回顧JVM菜鳥進階高手之路十三,問題現象就是相同的代碼,jvm參數不一樣,表現的現象不一樣。=75 通過jstat命令,查看結果如下:關於jstat命令詳情可以參考:https://docs.oracle.com/javase/8/docs/technotes/tools/unix/jstat.htmljvm參數調整如下:-Xmx20m-Xms20m
  • 想理解JVM看了這篇文章,就知道了!
    2|0JVM介紹2|1什麼是JVM作為java工程師,對於jvm肯定不陌生。JVM是Java Virtual Machine的縮寫,通俗來說也就是運行java代碼的容器。當項目啟動時,會根據jvm相關配置參數,在計算機的內存中開啟一片空間用於運行JVM。
  • SpringBoot2.x基礎篇:Linux後臺運行Jar以及Jvm參數調優
    07JVM調優腳本JVM的調優尤為最重,伺服器的配置有限,可使用的資源我們則是要珍惜,做出最大的貢獻!!!