JVM調(diào)優(yōu)

1 調(diào)優(yōu)層次

性能調(diào)優(yōu)包含多個(gè)層次,比如:架構(gòu)調(diào)優(yōu)、代碼調(diào)優(yōu)、JVM調(diào)優(yōu)、數(shù)據(jù)庫(kù)調(diào)優(yōu)、操作系統(tǒng)調(diào)優(yōu)等。
架構(gòu)調(diào)優(yōu)和代碼調(diào)優(yōu)是JVM調(diào)優(yōu)的基礎(chǔ),其中架構(gòu)調(diào)優(yōu)是對(duì)系統(tǒng)影響最大的。

2 調(diào)優(yōu)指標(biāo)

  • 吞吐量:運(yùn)行用戶代碼的時(shí)間占總運(yùn)行時(shí)間的比例 (總運(yùn)行時(shí)間=程序的運(yùn)行時(shí)間+內(nèi)存回收的時(shí)間);
  • 暫停時(shí)間:執(zhí)行垃圾收集時(shí),程序的工作線程被暫停的時(shí)間;
  • 內(nèi)存占用:java堆區(qū)所占的內(nèi)存大?。?/li>

這三者共同構(gòu)成一個(gè)“不可能三角”。三者總體的表現(xiàn)會(huì)隨著技術(shù)進(jìn)步而越來(lái)越好。一款優(yōu)秀的收集器通常最多同時(shí)滿足其中的兩項(xiàng)。

簡(jiǎn)單來(lái)說(shuō),主要抓住兩點(diǎn):

  • 吞吐量
    吞吐量?jī)?yōu)先,意味著在單位時(shí)間內(nèi),STW的時(shí)間最短
  • 暫停時(shí)間
    暫停時(shí)間優(yōu)先,意味這盡可能讓單次STW的時(shí)間最短

在設(shè)計(jì)(或使用)GC算法時(shí),必須確定我們的目標(biāo):一個(gè)GC算法只可能針對(duì)兩個(gè)目標(biāo)之一(即只專(zhuān)注于較大吞吐量或最小暫停時(shí)間),或嘗試找一個(gè)二者的折衷。

現(xiàn)在標(biāo)準(zhǔn),在最大吞吐量?jī)?yōu)先的情況下,降低停頓時(shí)間

3 JVM調(diào)優(yōu)原則

3.1 優(yōu)先原則

優(yōu)先架構(gòu)調(diào)優(yōu)和代碼調(diào)優(yōu),JVM優(yōu)化是不得已的手段,大多數(shù)的Java應(yīng)用不需要進(jìn)行JVM優(yōu)化

3.2 堆設(shè)置

參數(shù)-Xms和-Xmx,通常設(shè)置為相同的值,避免運(yùn)行時(shí)要不斷擴(kuò)展JVM內(nèi)存,建議擴(kuò)大至3-4倍FullGC后的老年代空間占用。

3.3 年輕代設(shè)置

參數(shù)-Xmn,1-1.5倍FullGC之后的老年代空間占用。

避免新生代設(shè)置過(guò)小,當(dāng)新生代設(shè)置過(guò)小時(shí),會(huì)帶來(lái)兩個(gè)問(wèn)題:一是minor GC次數(shù)頻繁,二是可能導(dǎo)致 minor GC對(duì)象直接進(jìn)老年代。當(dāng)老年代內(nèi)存不足時(shí),會(huì)觸發(fā)Full GC。
避免新生代設(shè)置過(guò)大,當(dāng)新生代設(shè)置過(guò)大時(shí),會(huì)帶來(lái)兩個(gè)問(wèn)題:一是老年代變小,可能導(dǎo)致Full GC頻繁執(zhí)行;二是 minor GC 執(zhí)行回收的時(shí)間大幅度增加。

3.4 老年代設(shè)置

  • 注重低延遲的應(yīng)用

    • 老年代使用并發(fā)收集器,所以其大小需要小心設(shè)置,一般要考慮并發(fā)會(huì)話率和會(huì)話持續(xù)時(shí)間等一些參數(shù)
    • 如果堆設(shè)置偏小,可能會(huì)造成內(nèi)存碎片、高回收頻率以及應(yīng)用暫停
    • 如果堆設(shè)置偏大,則需要較長(zhǎng)的收集時(shí)間
  • 吞吐量?jī)?yōu)先的應(yīng)用
    一般吞吐量?jī)?yōu)先的應(yīng)用都有一個(gè)較大的年輕代和一個(gè)較小的老年代。原因是,這樣可以盡可能回收掉大部分短期對(duì)象,減少中期的對(duì)象,而老年代盡可能存放長(zhǎng)期存活對(duì)象

3.5 方法區(qū)設(shè)置

基于jdk1.7版本,永久代:參數(shù)-XX:PermSize和-XX:MaxPermSize;
基于jdk1.8版本,元空間:參數(shù) -XX:MetaspaceSize和-XX:MaxMetaspaceSize;
通常設(shè)置為相同的值,避免運(yùn)行時(shí)要不斷擴(kuò)展,建議擴(kuò)大至1.2-1.5倍FullGc后的永久帶空間占用。

3.6 GC設(shè)置

3.6.1 GC發(fā)展階段

Serial?Parallel(并行) ?CMS(并發(fā))? G1?ZGC
截至jdk1.8 ,一共有7款不同垃圾收集器。每一款不同的垃圾收集器都有不同的特點(diǎn),在具體使用的時(shí)候,需要根據(jù)具體的情況選擇不同的垃圾回收器

垃圾收集器 分類(lèi) 作用位置 使用算法 特點(diǎn) 使用場(chǎng)景
Serial 串行 新生代 復(fù)制算法 響應(yīng)速度優(yōu)先 單CPU環(huán)境下client模式
ParNew 并行 新生代 復(fù)制算法 響應(yīng)速度優(yōu)先 多CPU環(huán)境下Server模式下與CMS配合使用
Parallel 并行 新生代 復(fù)制算法 吞吐量?jī)?yōu)先 適用于后臺(tái)運(yùn)算而不需要太多交互的場(chǎng)景
Serial Old 串行 老年代 標(biāo)記-整理 響應(yīng)速度優(yōu)先 單CPU環(huán)境下client模式
Parallel Old 并行 老年代 標(biāo)記-整理 吞吐量?jī)?yōu)先 適用于后臺(tái)運(yùn)算而不需要太多交互的場(chǎng)景
CMS 并發(fā) 老年代 標(biāo)記-清除 響應(yīng)速度優(yōu)先 互聯(lián)網(wǎng)或B/S業(yè)務(wù)
G1 并發(fā)、并行 新生代、老年代 復(fù)制算法、標(biāo)記-整理 響應(yīng)速度優(yōu)先 面向服務(wù)端應(yīng)用
01.png

3.6.2 G1的適用場(chǎng)景

  • 面向服務(wù)端應(yīng)用,針對(duì)具有大內(nèi)存、多處理器的機(jī)器。(在普通大小的堆里表現(xiàn)并不驚喜)
  • 最主要的應(yīng)用是需要低GC延遲并具有大堆的應(yīng)用程序提供解決方案(G1通過(guò)每次只清理一部分而不是全部Region的增量式清理來(lái)保證每次GC停頓時(shí)間不會(huì)過(guò)長(zhǎng))
  • 在堆大小約6GB或更大時(shí),可預(yù)測(cè)的暫停時(shí)間可以低于0.5秒
  • 用來(lái)替換掉JDK1.5中的CMS收集器,以下情況,使用G1可能比CMS好
    • 超過(guò)50% 的java堆被活動(dòng)數(shù)據(jù)占用
    • 對(duì)象分配頻率或年代提升頻率變化很大
    • GC停頓時(shí)間過(guò)長(zhǎng)(大于0.5至1秒)
  • 從經(jīng)驗(yàn)上來(lái)說(shuō),整體而言:
    • 小內(nèi)存應(yīng)用上,CMS大概率會(huì)優(yōu)于 G1;
    • 大內(nèi)存應(yīng)用上,G1則很可能更勝一籌。
      這個(gè)臨界點(diǎn)大概是在 6~8G 之間(經(jīng)驗(yàn)值)

3.6.3 其他收集器適用場(chǎng)景

  • 如果你想要最小化地使用內(nèi)存和并行開(kāi)銷(xiāo),請(qǐng)選擇Serial Old(老年代) + Serial(年輕代)
  • 如果你想要最大化應(yīng)用程序的吞吐量,請(qǐng)選擇Parallel Old(老年代) + Parallel(年輕代)
  • 如果你想要最小化GC的中斷或停頓時(shí)間,請(qǐng)選擇CMS(老年代) + ParNew(年輕代)

4 JVM調(diào)優(yōu)步驟

4.1 監(jiān)控分析

分析GC日志及dump文件,判斷是否需要優(yōu)化,確定瓶頸問(wèn)題點(diǎn)。

4.1.1 如何生成GC日志

常用參數(shù)部分會(huì)詳細(xì)講解如何生成GC日志

4.1.2 如何產(chǎn)生dump文件

4.1.2.1 JVM的配置文件中配置

JVM啟動(dòng)時(shí)增加兩個(gè)參數(shù):

# 出現(xiàn)OOME時(shí)生成堆dump:
-XX:+HeapDumpOnOutOfMemoryError
# 生成堆文件地址:
-XX:HeapDumpPath=/home/hadoop/dump/

4.1.2.2 jmap生成

發(fā)現(xiàn)程序異常前通過(guò)執(zhí)行指令,直接生成當(dāng)前JVM的dump文件

jmap -dump:file=文件名.dump [pid]
# 9257是指JVM的進(jìn)程號(hào)
jmap -dump:format=b,file=testmap.dump 9257

第一種方式是一種事后方式,需要等待當(dāng)前JVM出現(xiàn)問(wèn)題后才能生成dump文件,實(shí)時(shí)性不高;
第二種方式在執(zhí)行時(shí),JVM是暫停服務(wù)的,所以對(duì)線上的運(yùn)行會(huì)產(chǎn)生影響。

所以建議第一種方式。

4.1.2.3 第三方可視化工具生成

4.2 判斷

如果各項(xiàng)參數(shù)設(shè)置合理,系統(tǒng)沒(méi)有超時(shí)日志或異常信息出現(xiàn),GC頻率不高,GC耗時(shí)不高,那么沒(méi)有必要進(jìn)行GC優(yōu)化,如果GC時(shí)間超過(guò)1-3秒,或者頻繁GC,則必須優(yōu)化。
遇到以下情況,就需要考慮進(jìn)行JVM調(diào)優(yōu):

  • 系統(tǒng)吞吐量與響應(yīng)性能不高或下降;
  • Heap內(nèi)存(老年代)持續(xù)上漲達(dá)到設(shè)置的最大內(nèi)存值;
  • Full GC 次數(shù)頻繁;
  • GC 停頓時(shí)間過(guò)長(zhǎng)(超過(guò)1秒);
  • 應(yīng)用出現(xiàn)OutOfMemory等內(nèi)存異常;
  • 應(yīng)用中有使用本地緩存且占用大量?jī)?nèi)存空間;

4.3 確定目標(biāo)

調(diào)優(yōu)的最終目的都是為了應(yīng)用程序使用最小的硬件消耗來(lái)承載更大的吞吐量或者低延遲。
jvm調(diào)優(yōu)主要是針對(duì)垃圾收集器的收集性能優(yōu)化,減少GC的頻率和Full GC的次數(shù),令運(yùn)行在虛擬機(jī)上的應(yīng)用能夠使用更少的內(nèi)存、高吞吐量、低延遲。

下面列舉一些JVM調(diào)優(yōu)的量化目標(biāo)參考實(shí)例,注意:不同應(yīng)用的JVM調(diào)優(yōu)量化目標(biāo)是不一樣的。

  • 堆內(nèi)存使用率<=70%;
  • 老年代內(nèi)存使用率<=70%;
  • avgpause<=1秒;
  • Full GC次數(shù)0或avg pause interval>=24小時(shí) ;

4.4 調(diào)整參數(shù)

調(diào)優(yōu)一般是從滿足程序的內(nèi)存使用需求開(kāi)始的,之后是時(shí)間延遲的要求,最后才是吞吐量的要求。
要基于這個(gè)步驟來(lái)不斷優(yōu)化,每一個(gè)步驟都是進(jìn)行下一步的基礎(chǔ),不可逆行之。

4.5 對(duì)比調(diào)優(yōu)前后指標(biāo)差異

4.6 重復(fù)以上過(guò)程

4.7 應(yīng)用

找到合適的參數(shù),先在單臺(tái)服務(wù)器上試運(yùn)行,然后將這些參數(shù)應(yīng)用到所有服務(wù)器,并進(jìn)行后續(xù)跟蹤。

5 JVM調(diào)優(yōu)工具

5.1 jps

jps:JVM Process Status Tool
jps可以查看Java進(jìn)程,相當(dāng)于Linux下的ps命令,只不過(guò)它只列出Java進(jìn)程。

5.1.1 使用語(yǔ)法

jps:列出Java程序進(jìn)程ID和Main函數(shù)名稱
jps -q:只輸出進(jìn)程ID
jps -m:輸出傳遞給Java進(jìn)程(主函數(shù))的參數(shù)
jps -l:輸出主函數(shù)的完整路徑
jps -v:顯示傳遞給Java虛擬的參數(shù)

5.1.2 示例

02.png

5.2 jstat

jstat:JVM Statistics Monitoring Tool
jstat可以查看Java程序運(yùn)行時(shí)相關(guān)信息,可以通過(guò)它查看堆信息的相關(guān)情況

5.2.1 使用語(yǔ)法

jstat -<options> [-t] [-h<lines>] <vmid> [<interval> [<count>]]

5.2.2 options可選值

-class:顯示ClassLoader的相關(guān)信息
-compiler:顯示JIT編譯的相關(guān)信息
-gc:顯示與GC相關(guān)信息
-gccapacity:顯示各個(gè)代的容量和使用情況
-gccause:顯示垃圾收集相關(guān)信息(同-gcutil),同時(shí)顯示最后一次或當(dāng)前正在發(fā)生的垃圾收集的誘發(fā)原因
-gcnew:顯示新生代信息
-gcnewcapacity:顯示新生代大小和使用情況
-gcold:顯示老年代信息
-gcoldcapacity:顯示老年代大小
-gcpermcapacity:顯示永久代大小
-gcutil:顯示垃圾收集信息
-printcompilation:輸出JIT編譯的方法信息
-t:在輸出信息前加上一個(gè)Timestamp列,顯示程序的運(yùn)行時(shí)間
-h:可以在周期性數(shù)據(jù)輸出后,輸出多少行數(shù)據(jù)后,跟著一個(gè)表頭信息
interval:用于指定輸出統(tǒng)計(jì)數(shù)據(jù)的周期,單位為毫秒
count:用于指定一個(gè)輸出多少次數(shù)據(jù)

5.2.3 示例

示例一
顯示GC相關(guān)信息

jstat -gc 7063 500 4
7063 是進(jìn)程ID ,采樣時(shí)間間隔為500ms,采樣數(shù)為4
03.png
S0C:年輕代中第一個(gè)survivor(幸存區(qū))的容量 (字節(jié))
S1C:年輕代中第二個(gè)survivor(幸存區(qū))的容量 (字節(jié))
S0U:年輕代中第一個(gè)survivor(幸存區(qū))目前已使用空間 (字節(jié))
S1U:年輕代中第二個(gè)survivor(幸存區(qū))目前已使用空間 (字節(jié))
EC :年輕代中Eden(伊甸園)的容量 (字節(jié))
EU :年輕代中Eden(伊甸園)?前已使?空間 (字節(jié))
OC :Old代的容量 (字節(jié))
OU :Old代目前已使用空間 (字節(jié))
MC:metaspace(元空間)的容量 (字節(jié))
MU:metaspace(元空間)目前已使用空間 (字節(jié))
CCSC:壓縮類(lèi)空間大小
CCSU:壓縮類(lèi)空間使用大小
YGC :從應(yīng)用程序啟動(dòng)到采樣時(shí)年輕代中g(shù)c次數(shù)
YGCT :從應(yīng)用程序啟動(dòng)到采樣時(shí)年輕代中g(shù)c所用時(shí)間(s)
FGC :從應(yīng)用程序啟動(dòng)到采樣時(shí)old代(全gc)gc次數(shù)
FGCT :從應(yīng)用程序啟動(dòng)到采樣時(shí)old代(全gc)gc所用時(shí)間(s)
GCT:從應(yīng)用程序啟動(dòng)到采樣時(shí)gc用的總時(shí)間(s)

示例二
顯示垃圾收集相關(guān)信息

jstat -gcutil 7737 5s 5
04.png
S0 年輕代中第一個(gè)survivor(幸存區(qū))已使用的占當(dāng)前容量百分比
S1 年輕代中第二個(gè)survivor(幸存區(qū))已使用的占當(dāng)前容量百分比
E 年輕代中Eden(伊甸園)已使用的占當(dāng)前容量百分比
O old代已使用的占當(dāng)前容量百分比
M metaspace已使用的占當(dāng)前容量百分比
CCS 壓縮使用比例
YGC 從應(yīng)用程序啟動(dòng)到采樣時(shí)年輕代中g(shù)c次數(shù)
YGCT 從應(yīng)用程序啟動(dòng)到采樣時(shí)年輕代中g(shù)c所用時(shí)間(s)
FGC 從應(yīng)用程序啟動(dòng)到采樣時(shí)old代(全gc)gc次數(shù)
FGCT 從應(yīng)用程序啟動(dòng)到采樣時(shí)old代(全gc)gc所用時(shí)間(s)
GCT 從應(yīng)用程序啟動(dòng)到采樣時(shí)gc用的總時(shí)間(s)

5.3 jinfo

jinfo:Java Configuration Info
jinfo可以用來(lái)查看正在運(yùn)行的java程序的擴(kuò)展參數(shù),甚至支持運(yùn)行時(shí)修改部分參數(shù)

5.3.1 使用語(yǔ)法

jinfo [option] <pid>

5.3.2 option可選值

-flag <name> to print the value of the named VM flag
-flag [+|-]<name> to enable or disable the named VM flag
-flag <name>=<value> to set the named VM flag to the given value
-flags to print VM flags
-sysprops to print Java system properties
<no option> to print both of the above
-h | -help to print this help message

5.3.2 示例

示例一

查看堆的最大值

~ jinfo -flag MaxHeapSize 8384
-XX:MaxHeapSize=10485760

示例二

查看所有參數(shù)

~ jinfo -flags 8384
Attaching to process ID 8384, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 25.121-b13
Non-default VM flags: -XX:CICompilerCount=4 -XX:InitialHeapSize=10485760 -
XX:MaxHeapSize=10485760 -XX:MaxNewSize=3145728 -XX:MinHeapDeltaBytes=524288 -
XX:NewSize=3145728 -XX:OldSize=7340032 -XX:+UseCompressedClassPointers -
XX:+UseCompressedOops -XX:+UseFastUnorderedTimeStamps -XX:+UseParallelGC
Command line: -Xms10m -Xmx10m -Dfile.encoding=UTF-8

示例三

查看使用的垃圾回收器

~ jinfo -flag UseParallelGC 8384
-XX:+UseParallelGC
~ jinfo -flag UseConcMarkSweepGC 8384
-XX:-UseConcMarkSweepGC

示例四

設(shè)置日志打印

~ jinfo -flag PrintGCDetails 8384
-XX:-PrintGCDetails

~ jinfo -flag +PrintGCDetails 8384
~ jinfo -flag PrintGCDetails 8384
-XX:+PrintGCDetails

~ jinfo -flag -PrintGCDetails 8384
~ jinfo -flag PrintGCDetails 8384
-XX:-PrintGCDetails

5.4 jmap

jmap:Memory Map
jmap可以查看堆內(nèi)存使用狀況,一般結(jié)合jhat使用。

5.4.1 使用語(yǔ)法

 jmap [option] <pid>
        (to connect to running process)
 jmap [option] <executable <core>
        (to connect to a core file)
 jmap [option] [server_id@]<remote server IP or hostname>
        (to connect to remote debug server)

option:選項(xiàng)參數(shù)。
pid:需要打印配置信息的進(jìn)程ID。
executable:產(chǎn)生核心dump的Java可執(zhí)行文件。
core:需要打印配置信息的核心文件。
server-id:可選的唯一id,如果相同的遠(yuǎn)程主機(jī)上運(yùn)行了多臺(tái)調(diào)試服務(wù)器,用此選項(xiàng)參數(shù)標(biāo)識(shí)服務(wù)器。
remote server IP or hostname:遠(yuǎn)程調(diào)試服務(wù)器的IP地址或主機(jī)名。

5.4.2 option可選值

<none> to print same info as Solaris pmap
-heap to print java heap summary
-histo[:live] to print histogram of java object heap; if the "live" suboption is specified, only count live objects

-clstats to print class loader statistics
-finalizerinfo to print information on objects awaiting finalization
-dump:<dump-options> to dump java heap in hprof binary format
-F force. Use with -dump:<dump-options> <pid> or -histo to force a heap dump or histogram when <pid> does not respond. The "live" suboption is not supported in this mode.

-h | -help to print this help message
-J<flag> to pass <flag> directly to the runtime system

no option: 查看進(jìn)程的內(nèi)存映像信息,類(lèi)似 Solaris pmap 命令。
heap: 顯示Java堆詳細(xì)信息
histo[:live]: 顯示堆中對(duì)象的統(tǒng)計(jì)信息
clstats:打印類(lèi)加載器信息
finalizerinfo: 顯示在F-Queue隊(duì)列等待Finalizer線程執(zhí)行finalizer方法的對(duì)象
dump:<dump-options>:生成堆轉(zhuǎn)儲(chǔ)快照
F:當(dāng)-dump沒(méi)有響應(yīng)時(shí),使用-dump或者-histo參數(shù)。在這個(gè)模式下,live子參數(shù)無(wú)效
help:打印幫助信息
J<flag>:指定傳遞給運(yùn)行jmap的JVM的參數(shù)

dump-options可選值

-live dump only live objects; if not specified,all objects in the heap are dumped.
-format=b binary format
-file=<file> dump heap to <file>
Example:jmap -dump:live,format=b,file=heap.bin <pid>

5.4.3 示例

示例一

顯示Java堆詳細(xì)信息

jmap -heap pid

打印一個(gè)堆的摘要信息,包括使用的GC算法、堆配置信息和各內(nèi)存區(qū)域內(nèi)存使用信息

~ jmap -heap 8985
Attaching to process ID 8985, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 25.121-b13

using thread-local object allocation.
Parallel GC with 8 thread(s)

Heap Configuration:
 MinHeapFreeRatio = 0
 MaxHeapFreeRatio = 100
 MaxHeapSize = 524288000 (500.0MB)
 NewSize = 174587904 (166.5MB)
 MaxNewSize = 174587904 (166.5MB)
 OldSize = 349700096 (333.5MB)
 NewRatio = 2
 SurvivorRatio = 8
 MetaspaceSize = 21807104 (20.796875MB)
 CompressedClassSpaceSize = 1073741824 (1024.0MB)
 MaxMetaspaceSize = 17592186044415 MB
 G1HeapRegionSize = 0 (0.0MB)
 
Heap Usage:
PS Young Generation
Eden Space:
 capacity = 131596288 (125.5MB)
 used = 127090976 (121.20339965820312MB)
 free = 4505312 (4.296600341796875MB)
 96.57641407028137% used
From Space:
 capacity = 21495808 (20.5MB)
 used = 21477712 (20.482742309570312MB)
 free = 18096 (0.0172576904296875MB)
 99.91581614424543% used
To Space:
 capacity = 21495808 (20.5MB)
 used = 0 (0.0MB)
 free = 21495808 (20.5MB)
 0.0% used
PS Old Generation
 capacity = 349700096 (333.5MB)
 used = 100703528 (96.03836822509766MB)
 free = 248996568 (237.46163177490234MB)
 28.79711191157351% used
 
2156 interned Strings occupying 152440 bytes.

示例二

顯示堆中對(duì)象的統(tǒng)計(jì)信息

jmap -histo:live pid 

其中包括每個(gè)Java類(lèi)、對(duì)象數(shù)量、內(nèi)存大小(單位:字節(jié))、完全限定的類(lèi)名。打印的虛擬機(jī)內(nèi)部的類(lèi)名稱將會(huì)帶有一個(gè)’*’前綴。如果指定了live子選項(xiàng),則只計(jì)算活動(dòng)的對(duì)象。

~ jmap -histo:live 8985
 num #instances #bytes class name
----------------------------------------------
 1: 3682 339156840 [B
 2: 3806 408160 [C
 3: 3794 91056 java.lang.String
 4: 593 67480 java.lang.Class
 5: 587 54568 [Ljava.lang.Object;
 6: 3273 52368 com.kkb.example.HeapInstanceTest

示例三

打印類(lèi)加載器信息

jmap -clstats pid 

-clstats是-permstat的替代方案,在JDK8之前,-permstat用來(lái)打印類(lèi)加載器的數(shù)據(jù)打印Java堆內(nèi)存的永久保存區(qū)域的類(lèi)加載器的智能統(tǒng)計(jì)信息。對(duì)于每個(gè)類(lèi)加載器而言,它的名稱、活躍 度、地址、父類(lèi)加載器、它所加載的類(lèi)的數(shù)量和大小都會(huì)被打印。此外,包含的字符串?dāng)?shù)量和大小也會(huì)被打印。

~ jmap -clstats 8985
Attaching to process ID 8985, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 25.121-b13
finding class loader instances ..done.
computing per loader stat ..done.
please wait.. computing
liveness......................................................................
.............................done.
class_loader classes bytes parent_loader alive? type
<bootstrap> 517 969116 null live <internal>
0x00000007af095a08 0 0 0x00000007ae86f288 live 
java/util/ResourceBundle$RBClassLoader@0x00000007c00555e8
0x00000007ae86f288 9 29861 0x00000007ae8770f8 live 
sun/misc/Launcher$AppClassLoader@0x00000007c000f6a0
0x00000007ae8770f8 0 0 null live 
sun/misc/Launcher$ExtClassLoader@0x00000007c000fa48
total = 4 526 998977 N/A alive=4, dead=0 N/A

示例四

打印正等候回收的對(duì)象的信息

jmap -finalizerinfo pid 
~ jmap -finalizerinfo 10067
Attaching to process ID 10067, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 25.121-b13
Number of objects pending for finalization: 0

Number of objects pending for finalization: 0 說(shuō)明當(dāng)前F-QUEUE隊(duì)列中并沒(méi)有等待Fializer線程執(zhí)行final

示例五

生成堆轉(zhuǎn)儲(chǔ)快照dump文件

jmap -dump:format=b,file=heapdump.dump pid 

以hprof二進(jìn)制格式轉(zhuǎn)儲(chǔ)Java堆到指定filename的文件中。live子選項(xiàng)是可選的。如果指定了live 子選項(xiàng),堆中只有活動(dòng)的對(duì)象會(huì)被轉(zhuǎn)儲(chǔ)。想要瀏覽heap dump,你可以使用jhat(Java堆分析?具)讀取生成的文件。

這個(gè)命令執(zhí)行,JVM會(huì)將整個(gè)heap的信息dump寫(xiě)入到?個(gè)文件,heap如果比較大的話,就會(huì)導(dǎo)致這個(gè)過(guò)程比較耗時(shí),并且執(zhí)行的過(guò)程中為了保證dump的信息是可靠的,所以會(huì)暫停應(yīng)用, 線上系統(tǒng)慎用。

~ jmap -dump:format=b,file=heapdump.dump 10067 
Dumping heap to /Users/hadoop/heapdump.dump ...

5.5 jhat

jhat:Java Heap Analysis Tool

jhat 命令解析Java堆轉(zhuǎn)儲(chǔ)文件并啟動(dòng)一個(gè) web server,然后用瀏覽器來(lái)查看、瀏覽 dump 出來(lái)的 heap。

jhat 命令支持預(yù)先設(shè)計(jì)的查詢,比如顯示某個(gè)類(lèi)的所有實(shí)例。 還支持對(duì)象查詢語(yǔ)言(OQL, Object Query Language)。 OQL有點(diǎn)類(lèi)似SQL,專(zhuān)門(mén)用來(lái)查詢堆轉(zhuǎn)儲(chǔ)。

5.5.1 使用語(yǔ)法

jhat [ options ] heap-dump-file

5.5.2 options可選值

-stack false|true
關(guān)閉對(duì)象分配調(diào)用棧跟蹤(tracking object allocation call stack)。 如果分配位置信息在堆轉(zhuǎn)儲(chǔ)中不可用,則必須將此標(biāo)志設(shè)置為 false。 默認(rèn)值為 true 。

-refs false|true
關(guān)閉對(duì)象引用跟蹤(tracking of references to objects)。 默認(rèn)值為 true 。 默認(rèn)情況下, 返回的指針是指向其他特定對(duì)象的對(duì)象,如反向鏈接或輸入引用(referrers or incoming references), 會(huì)統(tǒng)計(jì)、計(jì)算堆中的所有對(duì)象。

-port port-number
設(shè)置 jhat HTTP server 的端口號(hào)。 默認(rèn)值 7000 。

-exclude exclude-file
指定對(duì)象查詢時(shí)需要排除的數(shù)據(jù)成員列表文件(a file that lists data members that should be excluded from the reachable objects query)。 例如, 如果文件列列出了 java.lang.String.value , 那么當(dāng)從某個(gè)特定對(duì)象 Object o 計(jì)算可達(dá)的對(duì)象列表時(shí), 引用路徑涉及 java.lang.String.value 的都會(huì)被排除。

-baseline exclude-file
指定一個(gè)基準(zhǔn)堆轉(zhuǎn)儲(chǔ)(baseline heap dump)。 在兩個(gè) heap dumps 中有相同 object ID 的對(duì)象會(huì)被標(biāo)記為不是新的(marked as not being new),其他對(duì)象被標(biāo)記為新的(new)。 在比較兩個(gè)不同的堆轉(zhuǎn)儲(chǔ)時(shí)很有用。

-debug int
設(shè)置 debug 級(jí)別。 0表示不輸出調(diào)試信息。 值越大則表示輸出更詳細(xì)的 debug 信息。

-version
啟動(dòng)后只顯示版本信息就退出

-h
顯示幫助信息并退出。 同 -help

-help
顯示幫助信息并退出。 同 -h

-J< flag >
因?yàn)閖hat命令實(shí)際上會(huì)啟動(dòng)一個(gè)JVM來(lái)執(zhí)行, 通過(guò) -J 可以在啟動(dòng)JVM時(shí)傳入一些啟動(dòng)參數(shù)。 例如, -J-Xmx512m 則指定運(yùn)行jhat的Java虛擬機(jī)使用的最大堆內(nèi)存為 512 MB。 如果需要使用多個(gè)JVM啟動(dòng)參數(shù),則傳多多個(gè) -Jxxxxxx。

5.5.3 示例

利用jhat分析剛剛jmap輸出的堆文件

05.png

這樣就啟動(dòng)起來(lái)了一個(gè)簡(jiǎn)易的HTTP服務(wù),端口號(hào)是7000,嘗試一下用瀏覽器訪問(wèn)?下它,本地的可以通過(guò)http://localhost:7000就可以得到這樣的頁(yè)面:

jhat啟動(dòng)后顯示的html頁(yè)面中包含有:

  • All classes including platform:顯示出堆中所包含的所有的類(lèi)
  • Show all members of the rootset :從根集能引用到的對(duì)象
  • Show instance counts for all classes (including platform/excluding platform):顯示平臺(tái)包括的所有類(lèi)的實(shí)例數(shù)量
  • Show heap histogram:堆實(shí)例的分布表
  • Show finalizer summary:Finalizer 摘要
  • Execute Object Query Language (OQL) query:執(zhí)行對(duì)象查詢語(yǔ)句(OQL)
select a from [I a where a.length > 256 //查詢長(zhǎng)度大于256的數(shù)組

5.6 jstack

jstack:Java Stack Trace

jstack是java虛擬機(jī)自帶的一種堆棧跟蹤工具。jstack用于生成java虛擬機(jī)當(dāng)前時(shí)刻的線程快照。線程快照是當(dāng)前java虛擬機(jī)內(nèi)每一條線程正在執(zhí)行的方法堆棧的集合,生成線程快照的主要目的是定位線程出現(xiàn)長(zhǎng)時(shí)間停頓的原因,如線程間死鎖、死循環(huán)、請(qǐng)求外部資源導(dǎo)致的長(zhǎng)時(shí)間等待等。 線程出現(xiàn)停頓的時(shí)候通過(guò)jstack來(lái)查看各個(gè)線程的調(diào)用堆棧,就可以知道沒(méi)有響應(yīng)的線程到底在后臺(tái)做什么事情,或者等待什么資源。

如果java程序崩潰生成core文件,jstack工具可以用來(lái)獲得core文件的java stack和native stack的信息,從而可以輕松地知道java程序是如何崩潰和在程序何處發(fā)生問(wèn)題。另外,jstack工具還可以附屬到正在運(yùn)行的java程序中,看到當(dāng)時(shí)運(yùn)行的java程序的java stack和native stack的信息, 如果現(xiàn)在運(yùn)行的java程序呈現(xiàn)hung的狀態(tài),jstack是非常有用的。

5.6.1 使用語(yǔ)法

jstack [ option ] pid 查看當(dāng)前時(shí)間點(diǎn),指定進(jìn)程的dump堆棧信息。
jstack [ option ] pid > 文件 將當(dāng)前時(shí)間點(diǎn)的指定進(jìn)程的dump堆棧信息,寫(xiě)入到指定文件中。
注:若該文件不存在,則會(huì)自動(dòng)生成;若該文件存在,則會(huì)覆蓋源文件。
jstack [ option ] executable core 查看當(dāng)前時(shí)間點(diǎn),core文件的dump堆棧信息。
jstack [ option ] [server_id@]<remote server IP or hostname> 查看當(dāng)前時(shí)間點(diǎn),遠(yuǎn)程機(jī)器的dump堆棧信息。

5.6.2 option可選值

-F 強(qiáng)制jstack。當(dāng)進(jìn)程掛起了,此時(shí)'jstack [-l] pid'是沒(méi)有響應(yīng)的,這時(shí)候可使用此參數(shù)來(lái)強(qiáng)制打印堆棧信息,一般情況不需要使用。
-m 打印java和native c/c++框架的所有棧信息??梢源蛴VM的堆棧,以及Native的棧幀,一般應(yīng)用排查不需要使用。
-l 長(zhǎng)列表。打印關(guān)于鎖的附加信息。例如屬于java.util.concurrent的ownable synchronizers列表,會(huì)使得JVM停頓得?久得多(可能會(huì)差很多倍,?如普通的jstack可能?毫秒和?次GC沒(méi)區(qū)別,加了-l 就是近一秒的時(shí)間),-l 建議不要用。一般情況不需要使用。
-h or -hel 打印幫助信息

在thread dump中,要留意下面幾種狀態(tài)

  • 死鎖,Deadlock(重點(diǎn)關(guān)注)
  • 等待資源,Waiting on condition(重點(diǎn)關(guān)注)
  • 等待獲取監(jiān)視器,Waiting on monitor entry(重點(diǎn)關(guān)注)
  • 阻塞,Blocked(重點(diǎn)關(guān)注)
  • 執(zhí)行中,Runnable
  • 暫停,Suspended
  • 對(duì)象等待中,Object.wait() 或 TIMED_WAITING
  • 停止,Parked

5.6.3 示例

示例一

06.png

示例二
將指定進(jìn)程的當(dāng)前堆棧情況記錄到某個(gè)?件中

07.png

示例三
統(tǒng)計(jì)線程數(shù)

08.png

示例四
檢測(cè)死鎖

09.png

10.png

5.7 hprof

hprof:Heap/CPU Profiling Tool 能夠展現(xiàn)CPU使用率,統(tǒng)計(jì)堆內(nèi)存使用情況。

J2SE中提供了一個(gè)簡(jiǎn)單的命令行工具來(lái)對(duì)java程序的cpu和heap進(jìn)行profiling,叫做HPROF。HPROF實(shí)際上是JVM中的一個(gè)native的庫(kù),它會(huì)在JVM啟動(dòng)的時(shí)候通過(guò)命令行參數(shù)來(lái)動(dòng)態(tài)加載,并成為JVM進(jìn)程的一部分。若要在java進(jìn)程啟動(dòng)的時(shí)候使用HPROF,用戶可以通過(guò)各種命令行參數(shù)類(lèi)型來(lái)使用HPROF對(duì)java進(jìn)程的heap或者(和)cpu進(jìn)行profiling的功能。HPROF產(chǎn)生的profiling數(shù)據(jù)可以是二進(jìn)制的,也可以是文本格式的。這些日志可以用來(lái)跟蹤和分析java進(jìn)程的性能問(wèn)題和瓶頸,解決內(nèi)存使用上不優(yōu)的地方或者程序?qū)崿F(xiàn)上的不優(yōu)之處。二進(jìn)制格式的日志還可以被JVM中的HAT工具來(lái)進(jìn)行瀏覽和分析,用以觀察java進(jìn)程的heap中各種類(lèi)型和數(shù)據(jù)的情況。在J2SE 5.0以后的版本中,HPROF已經(jīng)被并入到一個(gè)叫做Java Virtual Machine Tool Interface(JVM TI)中。

5.7.1 使用語(yǔ)法

java -agentlib:hprof[=options] ToBeProfiledClass
java -Xrunprof[:options] ToBeProfiledClass
javac -J-agentlib:hprof[=options] ToBeProfiledClass

5.7.2 options可選值

Option Name and Value   Description                     Default
---------------------   -----------                     -------
heap=dump|sites|all     heap profiling                  all
cpu=samples|times|old   CPU usage                       off
monitor=y|n             monitor contention              n
format=a|b              text(txt) or binary output      a
file=<file>             write data to file              java.hprof[.txt]
net=<host>:<port>       send data over a socket         off
depth=<size>            stack trace depth               4
interval=<ms>           sample interval in ms           10
cutoff=<value>          output cutoff point             0.0001
lineno=y|n              line number in traces?          y
thread=y|n              thread in traces?               n
doe=y|n                 dump on exit?                   y
msa=y|n                 Solaris micro state accounting  n
force=y|n               force output to <file>          y
verbose=y|n             print messages about dumps      y
11.png

5.7.3 示例

官方示例

CPU Usage Sampling Profiling(cpu=samples)的例子
下面每隔20毫秒采樣CPU消耗信息,堆棧深度為3,生成的profile文件名稱是java.hprof.txt,在當(dāng)前目錄。

java -agentlib:hprof=cpu=samples,interval=20,depth=3 Hello

CPU Usage Times Profiling(cpu=times)的例子
它相對(duì)于CPU Usage Sampling Profile能夠獲得更加細(xì)粒度的CPU消耗信息,能夠細(xì)到每個(gè)?法調(diào)?的開(kāi)始和結(jié)束,它的實(shí)現(xiàn)使用了字節(jié)碼注入技術(shù)(BCI)

javac -J-agentlib:hprof=cpu=times Hello.java

Heap Allocation Profiling(heap=sites)的例子

javac -J-agentlib:hprof=heap=sites Hello.java

Heap Dump(heap=dump)的例子
它能比上面的Heap Allocation Profiling生成更詳細(xì)的Heap Dump信息:

javac -J-agentlib:hprof=heap=dump Hello.java

雖然在JVM啟動(dòng)參數(shù)中加入-Xrunprof:heap=sites參數(shù)可以生成CPU/Heap Profile文件,但對(duì)JVM性
能影響非常大,不建議在線上服務(wù)器環(huán)境使用。

示例一

統(tǒng)計(jì)方法耗時(shí)

classes java -agentlib:hprof=cpu=times,interval=10 com.kkb.example.HprofTest
Dumping CPU usage by timing methods ... done.
classes vim java.hprof.txt
12.png

示例二

生成跟蹤點(diǎn)類(lèi)所占內(nèi)存百分比

classes java -agentlib:hprof=heap=sites com.kkb.example.HprofTest
Dumping allocation sites ... done.
classes vim java.hprof.txt
13.png

5.8 jconsole

Jconsole:Java Monitoring and Management Console,Java 5引入,一個(gè)內(nèi)置 Java 性能分析器,可以從命令行或在 GUI shell 中運(yùn)行。您可以輕松地使用 JConsole來(lái)監(jiān)控 Java 應(yīng)用程序性能和跟蹤Java 中的代碼。

5.8.1 如何啟動(dòng)JConsole

如果是從命令行啟動(dòng),使 JDK 在 PATH 上,運(yùn)行 jconsole 即可。
如果從 GUI shell 啟動(dòng),找到 JDK 安裝路徑,打開(kāi) bin 文件夾,雙擊 jconsole 。

當(dāng)分析工具彈出時(shí)(取決于正在運(yùn)行的 Java 版本以及正在運(yùn)行的 Java 程序數(shù)量),可能會(huì)出現(xiàn)一個(gè)對(duì)話框,要求輸入一個(gè)進(jìn)程的 URL 來(lái)連接,也可能列出許多不同的本地 Java 進(jìn)程(有時(shí)包含JConsole 進(jìn)程本身)來(lái)連接。
如下圖所示:想分析哪個(gè)程序就雙擊哪個(gè)進(jìn)程。

14.png

5.8.2 如何設(shè)置JAVA程序運(yùn)行時(shí)可以被JConsolse連接分析

本地程序(相對(duì)于開(kāi)啟JConsole的計(jì)算機(jī)),無(wú)需設(shè)置任何參數(shù)就可以被本地開(kāi)啟的JConsole連接(Java SE 6開(kāi)始無(wú)需設(shè)置,之前還是需要設(shè)置運(yùn)行時(shí)參數(shù) -Dcom.sun.management.jmxremote )

JConsole如何連接遠(yuǎn)程機(jī)器的JAVA程序?

jconsole 192.168.0.1:8999

也可以在已經(jīng)打開(kāi)的JConsole界面操作,連接?新建連接?選擇遠(yuǎn)程進(jìn)程?輸入遠(yuǎn)程主機(jī)IP和端口號(hào)?點(diǎn)擊“連接”,如下圖

15.png

5.8.3 示例

示例一
進(jìn)入視圖后包括這六個(gè)標(biāo)簽:

  • Overview: 顯示有關(guān)JVM和監(jiān)視值的概述信息
  • Memory: 顯示內(nèi)存使用信息
  • Threads: 顯示線程使用信息
  • Classes: 顯示類(lèi)裝載信息
  • VM Summary:顯示java VM信息
  • MBeans: 顯示 MBeans


    16.png

上圖描述有我們需要的信息,同時(shí)點(diǎn)擊右鍵可以保存數(shù)據(jù)到CSV文件。

內(nèi)存頁(yè)簽相對(duì)于可視化的jstat 命令,用于監(jiān)視受收集器管理的虛擬機(jī)內(nèi)存。

17.png

6 JVM參數(shù)

在JVM調(diào)整過(guò)程中,主要是對(duì)JVM參數(shù)做的調(diào)整,以下我們介紹主要參數(shù)。JVM參數(shù)有很多,其實(shí)我們直接使用默認(rèn)的JVM參數(shù),不去修改都可以滿足大多數(shù)情況。但是如果你想在有限的硬件資源下,部署的系統(tǒng)達(dá)到最大的運(yùn)行效率,那么進(jìn)行相關(guān)的JVM參數(shù)設(shè)置是必不可少的。

JVM參數(shù)主要分為以下三種:標(biāo)準(zhǔn)參數(shù)、非標(biāo)準(zhǔn)參數(shù)、不穩(wěn)定參數(shù)。

6.1 標(biāo)準(zhǔn)參數(shù)

標(biāo)準(zhǔn)參數(shù),顧名思義,標(biāo)準(zhǔn)參數(shù)中包括功能以及輸出的結(jié)果都是很穩(wěn)定的,基本上不會(huì)隨著JVM版本的變化而變化。

標(biāo)準(zhǔn)參數(shù)以-開(kāi)頭,如:java -version、java -jar等,通過(guò)java -help可以查詢所有的標(biāo)準(zhǔn)參數(shù),-help 也是?個(gè)標(biāo)準(zhǔn)參數(shù)。

6.2 非標(biāo)準(zhǔn)參數(shù)

非標(biāo)準(zhǔn)參數(shù)以-X開(kāi)頭,是標(biāo)準(zhǔn)參數(shù)的擴(kuò)展。對(duì)應(yīng)前面講的標(biāo)準(zhǔn)化參數(shù),這是非標(biāo)準(zhǔn)化參數(shù)。表示在將來(lái)的JVM版本中可能會(huì)發(fā)生改變,但是這類(lèi)以-X開(kāi)始的參數(shù)變化的比較小。
我們可以通過(guò) Java -X 命令來(lái)檢索所有-X 參數(shù)。

我們可以通過(guò)設(shè)置非標(biāo)準(zhǔn)參數(shù)來(lái)配置堆的內(nèi)存分配,常用的非標(biāo)準(zhǔn)參數(shù)有:

  • -Xmn新生代內(nèi)存的大小,包括Eden區(qū)和兩個(gè)Survivor區(qū)的總和,寫(xiě)法如:-Xmn1024,-Xmn1024k,-Xmn1024m,-Xmn1g 。
  • -Xms堆內(nèi)存的最小值,默認(rèn)值是總內(nèi)存/64(且小于1G)。默認(rèn)情況下,當(dāng)堆中可用內(nèi)存小于40%(這個(gè)值可以用-XX: MinHeapFreeRatio 調(diào)整,如-X:MinHeapFreeRatio=30)時(shí),堆內(nèi)存會(huì)開(kāi)始增加,?直增加到-Xmx的大小。
  • -Xmx堆內(nèi)存的最大值,默認(rèn)值是總內(nèi)存/4(且小于1G)。默認(rèn)情況下,當(dāng)堆中可用內(nèi)存大于70%(這個(gè)值可以用-XX: MaxHeapFreeRatio調(diào)整,如-X:MaxHeapFreeRatio =80)時(shí),堆內(nèi)存會(huì)開(kāi)始減少,一直減小到-Xms的大小。
    如果Xms和Xmx都不設(shè)置,則兩者大小會(huì)相同
  • -Xss每個(gè)線程的棧內(nèi)存,默認(rèn)1M,?般來(lái)說(shuō)是不需要改的。
  • -Xrs減少JVM對(duì)操作系統(tǒng)信號(hào)的使用。
  • -Xprof跟蹤正運(yùn)行的程序,并將跟蹤數(shù)據(jù)在標(biāo)準(zhǔn)輸出輸出。適合于開(kāi)發(fā)環(huán)境調(diào)試。
  • -Xnoclassgc關(guān)閉針對(duì)class的gc功能。因?yàn)槠渥柚羶?nèi)存回收,所以可能會(huì)導(dǎo)致OutOfMemoryError錯(cuò)誤,慎用。
  • -Xincgc開(kāi)啟增量gc(默認(rèn)為關(guān)閉)。這有助于減少長(zhǎng)時(shí)間GC時(shí)應(yīng)用程序出現(xiàn)的停頓,但由于可能和應(yīng)用程序并發(fā)執(zhí)行,所以會(huì)降低CPU對(duì)應(yīng)用的處理能力。
  • -Xloggc:file與-verbose:gc功能類(lèi)似,只是將每次GC事件的相關(guān)情況記錄到一個(gè)文件中,文件的位置最好在本地,以避免網(wǎng)絡(luò)的潛在問(wèn)題。

6.3 不穩(wěn)定參數(shù)

這是我們?nèi)粘i_(kāi)發(fā)中接觸到最多的參數(shù)類(lèi)型。這也是非標(biāo)準(zhǔn)化參數(shù),相對(duì)來(lái)說(shuō)不穩(wěn)定,隨著JVM版本的變化可能會(huì)發(fā)生變化,主要用于JVM調(diào)優(yōu)和debug。

不穩(wěn)定參數(shù)以-XX 開(kāi)頭,此類(lèi)參數(shù)的設(shè)置很容易引起JVM 性能上的差異,使JVM存在極大的不穩(wěn)定性。如果此類(lèi)參數(shù)設(shè)置合理將大大提高JVM的性能及穩(wěn)定性。

不穩(wěn)定參數(shù)分為三類(lèi)

  • 性能參數(shù):用于JVM的性能調(diào)優(yōu)和內(nèi)存分配控制,如內(nèi)存大小的設(shè)置
  • 行為參數(shù):用于改變JVM的基礎(chǔ)行為,如GC的方式和算法的選擇
  • 調(diào)試參數(shù):用于監(jiān)控、打印、輸出jvm的信息

不穩(wěn)定參數(shù)語(yǔ)法規(guī)則

  • 布爾類(lèi)型參數(shù)值:
    • -XX:+
    • -XX:-
      示例:-XX:+UseG1GC,表示啟用G1垃圾收集器
  • 數(shù)字類(lèi)型參數(shù)值:
    -XX:
    示例:-XX:MaxGCPauseMillis=500 ,表示設(shè)置GC的最大停頓時(shí)間是500ms
  • 字符串類(lèi)型參數(shù)值:
    -XX:
    示例:-XX:HeapDumpPath=./dump.core

6.4 常?參數(shù)

–Xms4g -Xmx4g –Xmn1200m –Xss512k 
-XX:NewRatio=4 -XX:SurvivorRatio=8 -XX:MaxTenuringThreshold=15 
-XX:PermSize=100m -XX:MaxPermSize=256m 
-XX:MaxDirectMemorySize=1G -XX:+DisableExplicitGC

參數(shù)解析:

  • -Xms4g:初始化堆內(nèi)存大小為4GB,ms是memory start的簡(jiǎn)稱,等價(jià)于-XX:InitialHeapSize。
  • -Xmx4g:堆內(nèi)存最大值為4GB,mx是memory max的簡(jiǎn)稱,等價(jià)于-XX:MaxHeapSize。
  • -Xmn1200m:設(shè)置年輕代大小為1200MB。增大年輕代后,將會(huì)減小老年代大小。此值對(duì)系統(tǒng)性能影響較大,Sun官方推薦配置為整個(gè)堆的3/8。
  • -Xss512k:設(shè)置每個(gè)線程的堆棧大小。JDK5.0以后每個(gè)線程堆棧大小為1MB,以前每個(gè)線程堆棧大小為256K。應(yīng)根據(jù)應(yīng)用線程所需內(nèi)存大小進(jìn)行調(diào)整。在相同物理內(nèi)存下,減小這個(gè)值能生成更多的線程。但是操作系統(tǒng)對(duì)一個(gè)進(jìn)程內(nèi)的線程數(shù)還是有限制的,不能無(wú)限生成,經(jīng)驗(yàn)值在3000~5000左右。
  • -XX:NewRatio=4:設(shè)置年輕代(包括Eden和兩個(gè)Survivor區(qū))與老年代的比值(除去持久代)。設(shè)置為4,則年輕代與老年代所占比值為1:4,年輕代占整個(gè)堆棧的1/5
  • -XX:SurvivorRatio=8:設(shè)置年輕代中Eden區(qū)與Survivor區(qū)的大小比值。設(shè)置為8,則兩個(gè)Survivor區(qū)與?個(gè)Eden區(qū)的比值為2:8,?個(gè)Survivor區(qū)占整個(gè)年輕代的1/10
  • -XX:PermSize=100m:初始化永久代大小為100MB。
  • -XX:MaxPermSize=256m:設(shè)置持久代大小為256MB。
  • -XX:MaxTenuringThreshold=15:設(shè)置垃圾最大年齡。如果設(shè)置為0的話,則年輕代對(duì)象不經(jīng)過(guò)Survivor區(qū),直接進(jìn)入老年代。對(duì)于老年代比較多的應(yīng)用,可以提高效率。如果將此值設(shè)置為?個(gè)較大值,則年輕代對(duì)象會(huì)在Survivor區(qū)進(jìn)行多次復(fù)制,這樣可以增加對(duì)象在年輕代的存活時(shí)間,增加在年輕代即被回收的概率。
  • -XX:MaxDirectMemorySize=1G:直接內(nèi)存。報(bào)java.lang.OutOfMemoryError: Direct buffermemory異??梢陨险{(diào)這個(gè)值。
  • -XX:+DisableExplicitGC:禁止運(yùn)行期顯式地調(diào)用System.gc()來(lái)觸發(fā)fulll GC。
    注意: Java RMI的定時(shí)GC觸發(fā)機(jī)制可通過(guò)配置-Dsun.rmi.dgc.server.gcInterval=86400來(lái)控制觸發(fā)的時(shí)間。
  • -XX:CMSInitiatingOccupancyFraction=60:老年代內(nèi)存回收閾值,默認(rèn)值為68。
  • -XX:ConcGCThreads=4:CMS垃圾回收器并行線程線,推薦值為CPU核心數(shù)。
  • -XX:ParallelGCThreads=8:新生代并行收集器的線程數(shù)。
  • -XX:CMSMaxAbortablePrecleanTime=500:當(dāng)abortable-preclean預(yù)清理階段執(zhí)行達(dá)到這個(gè)時(shí)間時(shí)就會(huì)結(jié)束。

新生代、老年代、永久代的參數(shù),如果不進(jìn)性指定,虛擬機(jī)會(huì)子動(dòng)選擇合適的值,同時(shí)也會(huì)基于系統(tǒng)的開(kāi)銷(xiāo)自動(dòng)調(diào)整

6.4.1 -XX:+PrintFlagsInitial、-XX:+PrintFlagsFinal

Java 6(update 21oder 21之后)版本, HotSpot JVM 提供給了兩個(gè)新的參數(shù),在JVM啟動(dòng)后,在命令行中可以輸出所有XX參數(shù)和值。
-XX:+PrintFlagsInitial:查看初始值
-XX:+PrintFlagsFinal:查看最終值(初始值可能被修改掉)
讓我們現(xiàn)在就了解一下新參數(shù)的輸出。以 -client 作為參數(shù)的 -XX:+ PrintFlagsFinal 的結(jié)果是一個(gè)按字母排序的590個(gè)參數(shù)表格(注意,每個(gè)release版本參數(shù)的數(shù)量會(huì)不一樣)

18.png

表格的每一行包括五列,來(lái)表示一個(gè)XX參數(shù)。第一列表示參數(shù)的數(shù)據(jù)類(lèi)型,第二列是名稱,第四列為值,第五列是參數(shù)的類(lèi)別。第三列”=”表示第四列是參數(shù)的默認(rèn)值,而”:=” 表明了參數(shù)被用戶或者JVM賦值了。

如果我們只想看下所有XX參數(shù)的默認(rèn)值,能夠用一個(gè)相關(guān)的參數(shù),-XX:+PrintFlagsInitial 。 用 -XX:+PrintFlagsInitial , 只是展示了第三列為“=”的數(shù)據(jù)(也包括那些被設(shè)置其他值的參數(shù))。

然而,注意當(dāng)與-XX:+PrintFlagsFinal 對(duì)比的時(shí)候,一些參數(shù)會(huì)丟失,大概因?yàn)檫@些參數(shù)是動(dòng)態(tài)創(chuàng)建的。

19.png

6.4.2 -XX:+PrintCommandLineFlags

讓我們看下這個(gè)參數(shù),事實(shí)上這個(gè)參數(shù)非常有用: -XX:+PrintCommandLineFlags 。這個(gè)參數(shù)讓JVM打印出那些已經(jīng)被用戶或者JVM設(shè)置過(guò)的詳細(xì)的XX參數(shù)的名稱和值。

換句話說(shuō),它列舉出 -XX:+PrintFlagsFinal的結(jié)果中第三列有":="的參數(shù)。以這種方式, 我們可以用-XX:+PrintCommandLineFlags作為快捷方式來(lái)查看修改過(guò)的參數(shù)??聪旅娴睦?。

20.png

現(xiàn)在如果我們每次啟動(dòng)java 程序的時(shí)候設(shè)置 -XX:+PrintCommandLineFlags 并且輸出到日志文件上,這樣會(huì)記錄下我們?cè)O(shè)置的JVM 參數(shù)對(duì)應(yīng)用程序性能的影響。

6.4.3 GC日志相關(guān)

設(shè)置JVM GC格式日志的主要參數(shù)包括如下8個(gè):

  1. -XX:+PrintGC 輸出簡(jiǎn)要GC日志
  2. -XX:+PrintGCDetails 輸出詳細(xì)GC日志
  3. -Xloggc:gc.log 輸出GC日志到文件
  4. -XX:+PrintGCTimeStamps 輸出GC的時(shí)間戳(以JVM啟動(dòng)到當(dāng)期的總時(shí)長(zhǎng)的時(shí)間戳形式)
  5. -XX:+PrintGCDateStamps 輸出GC的時(shí)間戳(以日期的形式,如 2020-04-26T21:53:59.234+0800)
  6. -XX:+PrintHeapAtGC 在進(jìn)行GC的前后打印出堆的信息
  7. -verbose:gc : 在JDK 8中,-verbose:gc是-XX:+PrintGC一個(gè)別稱,日志格式等價(jià)于:-XX:+PrintGC。不過(guò)在JDK 9中 -XX:+PrintGC被標(biāo)記為deprecated。
    -verbose:gc是一個(gè)標(biāo)準(zhǔn)的選項(xiàng),-XX:+PrintGC是一個(gè)實(shí)驗(yàn)的選項(xiàng),建議使用-verbose:gc 替代-XX:+PrintGC
  8. -XX:+PrintReferenceGC 打印年輕代各個(gè)引用的數(shù)量以及時(shí)長(zhǎng)

開(kāi)啟GC日志
多種方法都能開(kāi)啟GC的日志功能,其中包括:使用-verbose:gc或-XX:+PrintGC這兩個(gè)標(biāo)志中的任意一個(gè)能創(chuàng)建基本的GC日志(這兩個(gè)日志標(biāo)志實(shí)際上互為別名,默認(rèn)情況下的GC日志功能是關(guān)閉的)使用-XX:+PrintGCDetails標(biāo)志會(huì)創(chuàng)建更詳細(xì)的GC日志。
推薦使用-XX:+PrintGCDetails標(biāo)志(這個(gè)標(biāo)志默認(rèn)情況下也是關(guān)閉的);通常情況下使用基本的GC日志很難診斷垃圾回收時(shí)發(fā)生的問(wèn)題。

開(kāi)啟GC時(shí)間提示
除了使用詳細(xì)的GC日志,我們還推薦使用-XX:+PrintGCTimeStamps或者-XX:+PrintGCDateStamps,便于我們更精確地判斷幾次GC操作之間的時(shí)間。這兩個(gè)參數(shù)之間的差別在于時(shí)間戳是相對(duì)于0(依據(jù)JVM啟動(dòng)的時(shí)間)的值,而日期戳(date stamp)是實(shí)際的日期字符串。由于日期戳需要進(jìn)性格式化,所以它的效率可能會(huì)受輕微的影響,不過(guò)這種操作并不頻繁,它造成的影響也很難被我們感知。

指定GC日志路徑
默認(rèn)情況下GC日志直接輸出到標(biāo)準(zhǔn)輸出,不過(guò)使用-Xloggc:filename標(biāo)志也能修改輸出到某個(gè)文件。除了顯式地使用-PrintGCDetails標(biāo)志,否則使用-Xloggc會(huì)自動(dòng)地開(kāi)啟基本日志模式。

使用日志循環(huán)(Log rotation)標(biāo)志可以限制保存在GC日志中的數(shù)據(jù)量;對(duì)于需要長(zhǎng)時(shí)間運(yùn)行的服務(wù)器而言,這是一個(gè)非常有用的標(biāo)志,否則累積幾個(gè)月的數(shù)據(jù)很可能會(huì)耗盡服務(wù)器的磁盤(pán)。

開(kāi)啟日志滾動(dòng)輸出
通過(guò)-XX:+UseGCLogfileRotation -XX:NumberOfGCLogfiles=N -XX:GCLogfileSize=N標(biāo)志可以控制日志文件的循環(huán)。
默認(rèn)情況下,UseGCLogfileRotation標(biāo)志是關(guān)閉的。它負(fù)責(zé)打開(kāi)或關(guān)閉GC日志滾動(dòng)記錄功能的。要求必須設(shè)置 -Xloggc參數(shù)開(kāi)啟UseGCLogfileRotation標(biāo)志后,默認(rèn)的文件數(shù)目是0(意味著不作任何限制),默認(rèn)的日志文件大小是0(同樣也是不作任何限制)。因此,為了讓日志循環(huán)功能真正生效,我們必須為所有這些標(biāo)志設(shè)定值。

需要注意的是:

  • 設(shè)置滾動(dòng)日志文件的大小,必須大于8k。當(dāng)前寫(xiě)日志文件大小超過(guò)該參數(shù)值時(shí),日志將寫(xiě)入下一個(gè)文件
  • 設(shè)置滾動(dòng)日志文件的個(gè)數(shù),必須大于等于1
  • 必須設(shè)置 -Xloggc 參數(shù)

開(kāi)啟語(yǔ)句

-XX:+PrintGCDetails 
-XX:+PrintGCDateStamps 
-Xloggc:/home/hadoop/gc.log 
-XX:+UseGCLogFileRotation 
-XX:NumberOfGCLogFiles=10 
-XX:GCLogFileSize=512k

其他有用參數(shù)
-XX:+PrintGCApplicationStoppedTime 打印GC造成應(yīng)用暫停的時(shí)間
-XX:+PrintTenuringDistribution 在每次新生代 young GC時(shí),輸出幸存區(qū)中對(duì)象的年齡分布

日志含義

21.png
22.png

6.4.4 -XX:CMSFullGCsBeforeCompaction

CMSFullGCsBeforeCompaction 說(shuō)的是,在上一次CMS并發(fā)GC執(zhí)行過(guò)后,到底還要再執(zhí)行多少次full GC才會(huì)做壓縮。默認(rèn)是0,也就是在默認(rèn)配置下每次CMS GC頂不住了而要轉(zhuǎn)入full GC的時(shí)候都會(huì)做壓縮。 如果把CMSFullGCsBeforeCompaction配置為10,就會(huì)讓上面說(shuō)的第一個(gè)條件變成每隔10次真正的full GC才做一次壓縮(而不是每10次CMS并發(fā)GC就做一次壓縮,目前VM里沒(méi)有這樣的參數(shù))。這會(huì)使full GC更少做壓縮,也就更容易使CMS的old gen受碎片化問(wèn)題的困擾。 本來(lái)這個(gè)參數(shù)就是用來(lái)配置降低full GC壓縮的頻率,以期減少某些full GC的暫停時(shí)間。CMS回退到full GC時(shí)用的算法是mark-sweep-compact,但compaction是可選的,不做的話碎片化會(huì)嚴(yán)重些但這次full GC的暫停時(shí)間會(huì)短些。這是個(gè)取舍。

-XX:+UseCMSCompactAtFullCollection
-XX:CMSFullGCsBeforeCompaction=10

兩個(gè)參數(shù)必須同時(shí)使用才能生效。

6.4.5 -XX:HeapDumpPath

堆內(nèi)存出現(xiàn)OOM的概率是所有內(nèi)存耗盡異常中最高的,出錯(cuò)時(shí)的堆內(nèi)信息對(duì)解決問(wèn)題非常有幫助,所以給JVM設(shè)置這個(gè)參數(shù)(-XX:+HeapDumpOnOutOfMemoryError),讓JVM遇到OOM異常時(shí)能輸出堆內(nèi)信息,并通過(guò)(-XX:+HeapDumpPath)參數(shù)設(shè)置堆內(nèi)存溢出快照輸出的文件地址,這對(duì)于特別是對(duì)相隔數(shù)月才出現(xiàn)的OOM異常來(lái)說(shuō)尤為重要。
這兩個(gè)參數(shù)通常配套使用:

-XX:+HeapDumpOnOutOfMemoryError 
-XX:HeapDumpPath=./

6.4.6 -XX:OnOutOfMemoryError

-XX:OnOutOfMemoryError=
"/Library/Java/JavaVirtualMachines/jdk1.8.0_121.jdk/Contents/Home/binjconsole"

表示發(fā)生OOM后,運(yùn)行jconsole程序。這里可以不用加“”,因?yàn)閖console.exe路徑Program Files含有空格。

利用這個(gè)參數(shù),我們可以在系統(tǒng)OOM后,自定義一個(gè)腳本,可以用來(lái)發(fā)送郵件告警信息,可以用來(lái)重啟系統(tǒng)等等。

6.4.7 XX:InitialCodeCacheSize

JVM一個(gè)有趣的,但往往被忽視的內(nèi)存區(qū)域是“代碼緩存”,它是用來(lái)存儲(chǔ)已編譯方法生成的本地代碼。代碼緩存確實(shí)很少引起性能問(wèn)題,但是一旦發(fā)生其影響可能是毀滅性的。如果代碼緩存被占滿,JVM會(huì)打印出一條警告消息,并切換到interpreted-only 模式:JIT編譯器被停用,字節(jié)碼將不再會(huì)被編譯成機(jī)器碼。因此,應(yīng)用程序?qū)⒗^續(xù)運(yùn)行,但運(yùn)行速度會(huì)降低一個(gè)數(shù)量級(jí),直到有人注意到這個(gè)問(wèn)題。

就像其他內(nèi)存區(qū)域一樣,我們可以自定義代碼緩存的大小。相關(guān)的參數(shù)是-XX:InitialCodeCacheSize 和-
XX:ReservedCodeCacheSize,它們的參數(shù)和上面介紹的參數(shù)一樣,都是字節(jié)值。

6.4.8 -XX:+UseCodeCacheFlushing

如果代碼緩存不斷增長(zhǎng),例如,因?yàn)闊岵渴鹨鸬膬?nèi)存泄漏,那么提高代碼的緩存大小只會(huì)延緩其發(fā)生溢出。

為了避免這種情況的發(fā)生,我們可以嘗試一個(gè)有趣的新參數(shù):當(dāng)代碼緩存被填滿時(shí)讓JVM放棄一些編譯代碼。通過(guò)使用-XX:+UseCodeCacheFlushing 這個(gè)參數(shù),我們至少可以避免當(dāng)代碼緩存被填滿的時(shí)候JVM切換到interpreted-only 模式。

不過(guò),我仍建議盡快解決代碼緩存問(wèn)題發(fā)生的根本原因,如找出內(nèi)存泄漏并修復(fù)它。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • JVM 調(diào)優(yōu)概述 性能定義 吞吐量 - 指不考慮 GC 引起的停頓時(shí)間或內(nèi)存消耗,垃圾收集器能支撐應(yīng)用達(dá)到的最高性...
    裘馬輕狂大帥閱讀 321評(píng)論 0 1
  • JVM 調(diào)優(yōu)概述 性能定義 吞吐量 - 指不考慮 GC 引起的停頓時(shí)間或內(nèi)存消耗,垃圾收集器能支撐應(yīng)用達(dá)到的最高性...
    裘馬輕狂大帥閱讀 295評(píng)論 0 1
  • 運(yùn)用jvm自帶的命令可以方便的在生產(chǎn)監(jiān)控和打印堆棧的日志信息幫忙我們來(lái)定位問(wèn)題!雖然jvm調(diào)優(yōu)成熟的工具已經(jīng)有很多...
    王知無(wú)閱讀 834評(píng)論 1 1
  • 何時(shí)進(jìn)行JVM調(diào)優(yōu) Heap內(nèi)存(老年代)持續(xù)上漲達(dá)到設(shè)置的最大內(nèi)存值; Full GC 次數(shù)頻繁; GC 停頓時(shí)...
    請(qǐng)不要問(wèn)我是誰(shuí)閱讀 326評(píng)論 0 0
  • JVM調(diào)優(yōu)(一) 本片內(nèi)容 我們?yōu)槭裁匆獙?duì)JVM優(yōu)化 掌握jvm的運(yùn)行參數(shù)以及參數(shù)的設(shè)置 掌握jvm的內(nèi)存模型(堆...
    SuBHFeng閱讀 239評(píng)論 0 0

友情鏈接更多精彩內(nèi)容