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)用 |

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 示例

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

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

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輸出的堆文件

這樣就啟動(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 示例
示例一

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

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

示例四
檢測(cè)死鎖


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

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

示例二
生成跟蹤點(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

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)程。

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)擊“連接”,如下圖

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)存。

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ì)不一樣)

表格的每一行包括五列,來(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)建的。

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ù)??聪旅娴睦?。

現(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è):
- -XX:+PrintGC 輸出簡(jiǎn)要GC日志
- -XX:+PrintGCDetails 輸出詳細(xì)GC日志
- -Xloggc:gc.log 輸出GC日志到文件
- -XX:+PrintGCTimeStamps 輸出GC的時(shí)間戳(以JVM啟動(dòng)到當(dāng)期的總時(shí)長(zhǎng)的時(shí)間戳形式)
- -XX:+PrintGCDateStamps 輸出GC的時(shí)間戳(以日期的形式,如 2020-04-26T21:53:59.234+0800)
- -XX:+PrintHeapAtGC 在進(jìn)行GC的前后打印出堆的信息
- -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 - -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ì)象的年齡分布
日志含義


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ù)它。
