前言
經過上一篇的 JVM
垃圾回收知識,咱們瞭解了 JVM
具體的 垃圾回收算法 和幾種 垃圾回收器。理論是指導實踐的工具,有了理論指導,定位問題的時候,知識和經驗是關鍵基礎,數據能夠爲咱們提供依據。html
在線上咱們常常會碰見以下幾個問題:java
- 內存泄露;
- 某個進程忽然
CPU
飆升;
- 線程死鎖;
- 響應變慢。
若是遇到了以上這種問題,在 線下環境 能夠有各類 可視化的本地工具 支持查看。可是一旦到 線上環境,就沒有這麼多的 本地調試工具 支持,咱們該如何基於 監控工具 來進行定位問題?算法
咱們通常會基於 數據收集 來定位問題,而數據的收集離不開 監控工具 的處理,好比:運行日誌、異常堆棧、GC
日誌、線程快照、堆內存快照 等。爲了解決以上問題,咱們經常使用的 JVM
性能調優監控工具 大體有:jps
、jstat
、jstack
、jmap
、jhat
、hprof
、jinfo
。spring
正文
若是想要查看 Java
進程中 線程堆棧 的信息,能夠選擇 jstack
命令。若是要查看 堆內存,可使用 jmap
導出並使用 jhat
來進行分析,包括查看 類的加載信息,GC
算法,對象 的使用狀況等。可使用 jstat
來對 JVM
進行 統計監測,包括查看各個 區內存 和 GC
的狀況,還可使用 hprof
查看 CPU
使用率,統計 堆內存 使用狀況。下面會詳細的介紹這幾個工具的用法。數據庫
JVM常見監控工具 & 指令
1. jps進程監控工具
jps
是用於查看有權訪問的 hotspot
虛擬機 的進程。當未指定 hostid
時,默認查看 本機 jvm
進程,不然查看指定的 hostid
機器上的 jvm
進程,此時 hostid
所指機器必須開啓 jstatd
服務。編程
jps
能夠列出 jvm
進程 lvmid
,主類類名,main
函數參數, jvm
參數,jar
名稱等信息。後端
命令格式以下:api
1 2 3 4 5
|
usage: jps [-help] jps [-q] [-mlvV] [<hostid>]
Definitions: <hostid>: <hostname>[:<port>]
|
參數含義以下:數組
- -q: 不輸出 類名稱、
Jar
名稱 和傳入 main
方法的 參數;
- -l: 輸出
main
類或 Jar
的 全限定名稱;
- -m: 輸出傳入
main
方法的 參數;
- -v: 輸出傳入
JVM
的參數。
2. jinfo配置信息查看工具
jinfo
(JVM Configuration info
)這個命令做用是實時查看和調整 虛擬機運行參數。以前的 jps -v
命令只能查看到顯示 指定的參數,若是想要查看 未顯示 的參數的值就要使用 jinfo
命令。瀏覽器
1 2 3 4 5 6 7
|
Usage: jinfo [option] <pid> (to connect to running process) jinfo [option] <executable <core> (to connect to a core file) jinfo [option] [server_id@]<remote server IP or hostname> (to connect to remote debug server)
|
參數含義以下:
- pid:本地
jvm
服務的進程 ID
;
- executable core:打印 堆棧跟蹤 的核心文件;
- remote server IP/hostname:遠程
debug
服務的 主機名 或 IP
地址;
- server id:遠程
debug
服務的 進程 ID
。
參數選項說明以下:
參數 |
參數含義 |
flag |
輸出指定 args 參數的值 |
flags |
不須要 args 參數,輸出全部 JVM 參數的值 |
sysprops |
輸出系統屬性,等同於 System.getProperties() |
1 2 3 4 5 6 7
|
$ jinfo -flags 31983 Attaching to process ID 31983, please wait… Debugger attached successfully. Server compiler detected. JVM version is 25.91-b14 Non-default VM flags: -XX:CICompilerCount=3 -XX:InitialHeapSize=20971520 -XX:MaxHeapFreeRatio=90 -XX:MaxHeapSize=20971520 -XX:MaxNewSize=2097152 -XX:MinHeapDeltaBytes=524288 -XX:NewSize=2097152 -XX:OldSize=18874368 -XX:+PrintGC -XX:+PrintGCDetails -XX:+UseCompressedClassPointers -XX:+UseCompressedOops -XX:+UseFastUnorderedTimeStamps -XX:+UseParallelGC Command line: -Xmx20m -Xms20m -Xmn2m -javaagent:/opt/idea-IU-181.4668.68/lib/idea_rt.jar=34989:/opt/idea-IU-181.4668.68/bin -Dfile.encoding=UTF-8
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64
|
$ jinfo 31983 Attaching to process ID 31983, please wait... Debugger attached successfully. Server compiler detected. JVM version is 25.91-b14 Java System Properties:
java.runtime.name = Java(TM) SE Runtime Environment java.vm.version = 25.91-b14 sun.boot.library.path = /opt/jdk1.8.0_91/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 = /home/linchen/projects java.vm.specification.name = Java Virtual Machine Specification java.runtime.version = 1.8.0_91-b14 java.awt.graphicsenv = sun.awt.X11GraphicsEnvironment os.arch = amd64 java.endorsed.dirs = /opt/jdk1.8.0_91/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/lib java.specification.name = Java Platform API Specification java.class.version = 52.0 sun.management.compiler = HotSpot 64-Bit Tiered Compilers os.version = 4.15.0-24-generic user.home = /home/linchen user.timezone = java.awt.printerjob = sun.print.PSPrinterJob file.encoding = UTF-8 java.specification.version = 1.8 user.name = linchen java.class.path = /opt/jdk1.8.0_91/jre/lib/charsets.jar:/opt/jdk1.8.0_91/jre/lib/deploy.jar:/opt/jdk1.8.0_91/jre/lib/ext/cldrdata.jar:/opt/jdk1.8.0_91/jre/lib/ext/dnsns.jar:/opt/jdk1.8.0_91/jre/lib/ext/jaccess.jar:/opt/jdk1.8.0_91/jre/lib/ext/jfxrt.jar:/opt/jdk1.8.0_91/jre/lib/ext/localedata.jar:/opt/jdk1.8.0_91/jre/lib/ext/nashorn.jar:/opt/jdk1.8.0_91/jre/lib/ext/sunec.jar:/opt/jdk1.8.0_91/jre/lib/ext/sunjce_provider.jar:/opt/jdk1.8.0_91/jre/lib/ext/sunpkcs11.jar:/opt/jdk1.8.0_91/jre/lib/ext/zipfs.jar:/opt/jdk1.8.0_91/jre/lib/javaws.jar:/opt/jdk1.8.0_91/jre/lib/jce.jar:/opt/jdk1.8.0_91/jre/lib/jfr.jar:/opt/jdk1.8.0_91/jre/lib/jfxswt.jar:/opt/jdk1.8.0_91/jre/lib/jsse.jar:/opt/jdk1.8.0_91/jre/lib/management-agent.jar:/opt/jdk1.8.0_91/jre/lib/plugin.jar:/opt/jdk1.8.0_91/jre/lib/resources.jar:/opt/jdk1.8.0_91/jre/lib/rt.jar:/home/linchen/IdeaProjects/core_java/target/classes:/home/linchen/.m2/repository/io/netty/netty-all/4.1.7.Final/netty-all-4.1.7.Final.jar:/home/linchen/.m2/repository/junit/junit/4.12/junit-4.12.jar:/home/linchen/.m2/repository/org/hamcrest/hamcrest-core/1.3/hamcrest-core-1.3.jar:/home/linchen/.m2/repository/com/lmax/disruptor/3.3.0/disruptor-3.3.0.jar:/home/linchen/.m2/repository/com/rabbitmq/amqp-client/5.3.0/amqp-client-5.3.0.jar:/home/linchen/.m2/repository/org/slf4j/slf4j-api/1.7.25/slf4j-api-1.7.25.jar:/opt/idea-IU-181.4668.68/lib/idea_rt.jar java.vm.specification.version = 1.8 sun.arch.data.model = 64 sun.java.command = com.own.learn.jvm.JinfoTest java.home = /opt/jdk1.8.0_91/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_91 java.ext.dirs = /opt/jdk1.8.0_91/jre/lib/ext:/usr/java/packages/lib/ext sun.boot.class.path = /opt/jdk1.8.0_91/jre/lib/resources.jar:/opt/jdk1.8.0_91/jre/lib/rt.jar:/opt/jdk1.8.0_91/jre/lib/sunrsasign.jar:/opt/jdk1.8.0_91/jre/lib/jsse.jar:/opt/jdk1.8.0_91/jre/lib/jce.jar:/opt/jdk1.8.0_91/jre/lib/charsets.jar:/opt/jdk1.8.0_91/jre/lib/jfr.jar:/opt/jdk1.8.0_91/jre/classes java.vendor = Oracle Corporation file.separator = / java.vendor.url.bug = http://bugreport.sun.com/bugreport/ sun.io.unicode.encoding = UnicodeLittle sun.cpu.endian = little sun.desktop = gnome sun.cpu.isalist =
VM Flags: Non-default VM flags: -XX:CICompilerCount=3 -XX:InitialHeapSize=20971520 -XX:MaxHeapFreeRatio=90 -XX:MaxHeapSize=20971520 -XX:MaxNewSize=2097152 -XX:MinHeapDeltaBytes=524288 -XX:NewSize=2097152 -XX:OldSize=18874368 -XX:+PrintGC -XX:+PrintGCDetails -XX:+UseCompressedClassPointers -XX:+UseCompressedOops -XX:+UseFastUnorderedTimeStamps -XX:+UseParallelGC Command line: -Xmx20m -Xms20m -Xmn2m -javaagent:/opt/idea-IU-181.4668.68/lib/idea_rt.jar=34989:/opt/idea-IU-181.4668.68/bin -Dfile.encoding=UTF-8
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
|
$ jinfo -sysprops 31983 Attaching to process ID 31983, please wait… Debugger attached successfully. Server compiler detected. JVM version is 25.91-b14 java.runtime.name = Java(TM) SE Runtime Environment java.vm.version = 25.91-b14 sun.boot.library.path = /opt/jdk1.8.0_91/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 = /home/linchen/projects java.vm.specification.name = Java Virtual Machine Specification java.runtime.version = 1.8.0_91-b14 java.awt.graphicsenv = sun.awt.X11GraphicsEnvironment os.arch = amd64 java.endorsed.dirs = /opt/jdk1.8.0_91/jre/lib/endorsed java.io.tmpdir = /tmp line.separator =
|
2. jstat信息統計監控工具
jstat
是用於識別 虛擬機 各類 運行狀態信息 的命令行工具。它能夠顯示 本地 或者 遠程虛擬機 進程中的 類裝載、內存、垃圾收集、jit
編譯 等運行數據,它是 線上 定位 jvm
性能 的首選工具。
jstat
工具提供以下的 jvm
監控功能:
- 類的加載 及 卸載 的狀況;
- 查看 新生代、老生代 及 元空間(
MetaSpace
)的 容量 及使用狀況;
- 查看 新生代、老生代 及 元空間(
MetaSpace
)的 垃圾回收狀況,包括垃圾回收的 次數,垃圾回收所佔用的 時間;
- 查看 新生代 中
Eden
區及 Survior
區中 容量 及 分配狀況 等。
命令格式以下:
1 2
|
Usage: jstat -help|-options jstat -<option> [-t] [-h<lines>] <vmid> [<interval> [<count>]]
|
參數含義以下:
- option: 參數選項。
- -t: 能夠在打印的列加上
timestamp
列,用於顯示系統運行的時間。
- -h: 能夠在 週期性數據 的時候,能夠在指定輸出多少行之後輸出一次 表頭。
- vmid: Virtual Machine ID(進程的
pid
)。
- lines: 表頭 與 表頭 的間隔行數。
- interval: 執行每次的 間隔時間,單位爲 毫秒。
- count: 用於指定輸出記錄的 次數,缺省則會一直打印。
參數選項說明以下:
- class: 顯示 類加載
ClassLoad
的相關信息;
- compiler: 顯示
JIT
編譯 的相關信息;
- gc: 顯示和
gc
相關的 堆信息;
- gccapacity: 顯示 各個代 的 容量 以及 使用狀況;
- gcmetacapacity: 顯示 元空間
metaspace
的大小;
- gcnew: 顯示 新生代 信息;
- gcnewcapacity: 顯示 新生代大小 和 使用狀況;
- gcold: 顯示 老年代 和 永久代 的信息;
- gcoldcapacity: 顯示 老年代 的大小;
- gcutil: 顯示 垃圾回收信息;
- gccause: 顯示 垃圾回收 的相關信息(同
-gcutil
),同時顯示 最後一次 或 當前 正在發生的垃圾回收的 誘因;
- printcompilation: 輸出
JIT
編譯 的方法信息;
2.1. class
顯示和監視 類裝載、卸載數量、總空間 以及 耗費的時間。
1 2 3
|
$ jstat -class 8615 Loaded Bytes Unloaded Bytes Time 7271 13325.8 1 0.9 2.98
|
參數列表及含義以下:
參數 |
參數含義 |
Loaded |
已經裝載的類的數量 |
Bytes |
裝載類所佔用的字節數 |
Unloaded |
已經卸載類的數量 |
Bytes |
卸載類的字節數 |
Time |
裝載和卸載類所花費的時間 |
2.2. compiler
顯示虛擬機 實時編譯(JIT
)的 次數 和 耗時 等信息。
1 2 3
|
$ jstat -compiler 8615 Compiled Failed Invalid Time FailedType FailedMethod 3886 0 0 1.29 0
|
參數列表及含義以下:
參數 |
參數含義 |
Compiled |
編譯任務執行數量 |
Failed |
編譯任務執行失敗數量 |
Invalid |
編譯任務執行失效數量 |
Time |
編譯任務消耗時間 |
FailedType |
最後一個編譯失敗任務的類型 |
FailedMethod |
最後一個編譯失敗任務所在的類及方法 |
2.3. gc
顯示 垃圾回收(gc
)相關的 堆信息,查看 gc
的 次數 及 時間。
1 2 3
|
$ jstat -gc 8615 S0C S1C S0U S1U EC EU OC OU MC MU CCSC CCSU YGC YGCT FGC FGCT GCT 20480.0 10752.0 0.0 0.0 262128.0 130750.7 165376.0 24093.7 35456.0 33931.0 4992.0 4582.0 5 0.056 2 0.075 0.131
|
好比下面輸出的是 GC
信息,採樣 時間間隔 爲 250ms
,採樣數爲 4
:
1 2 3 4 5 6
|
$ jstat -gc 8615 250 4 S0C S1C S0U S1U EC EU OC OU MC MU CCSC CCSU YGC YGCT FGC FGCT GCT 20480.0 10752.0 0.0 0.0 262144.0 130750.7 165376.0 24093.7 35456.0 33931.0 4992.0 4582.0 5 0.056 2 0.075 0.131 20480.0 10752.0 0.0 0.0 262872.0 130750.7 165376.0 24093.7 35456.0 33931.0 4992.0 4582.0 5 0.056 2 0.075 0.131 20480.0 10752.0 0.0 0.0 262720.0 130750.7 165376.0 24093.7 35456.0 33931.0 4992.0 4582.0 5 0.056 2 0.075 0.131 20480.0 10752.0 0.0 0.0 262446.0 130750.7 165376.0 24093.7 35456.0 33931.0 4992.0 4582.0 5 0.056 2 0.075 0.131
|
參數列表及含義以下:
參數 |
參數含義 |
S0C |
年輕代中第一個 survivor 的容量 |
S1C |
年輕代中第二個 survivor 的容量 |
S0U |
年輕代中第一個 survivor 目前已使用空間 |
S1U |
年輕代中第二個 survivor 目前已使用空間 |
EC |
年輕代中 Eden 的容量 |
EU |
年輕代中 Eden 目前已使用空間 |
OC |
老年代的容量 |
OU |
老年代目前已使用空間 |
MC |
元空間 metaspace 的容量 |
MU |
元空間 metaspace 目前已使用空間 |
YGC |
從應用程序啓動到採樣時 年輕代 中 gc 次數 |
YGCT |
從應用程序啓動到採樣時 年輕代 中 gc 所用時間 |
FGC |
從應用程序啓動到採樣時 老年代 中 gc 次數 |
FGCT |
從應用程序啓動到採樣時 老年代 中 gc 所用時間 |
GCT |
從應用程序啓動到採樣時 gc 用的 總時間 |
2.4. gccapacity
顯示 虛擬機內存 中三代 年輕代(young
),老年代(old
),元空間(metaspace
)對象的使用和佔用大小。
1 2 3
|
$ jstat -gccapacity 8615 NGCMN NGCMX NGC S0C S1C EC OGCMN OGCMX OGC OC MCMN MCMX MC CCSMN CCSMX CCSC YGC FGC 87040.0 1397760.0 372736.0 20480.0 10752.0 262144.0 175104.0 2796544.0 165376.0 165376.0 0.0 1079296.0 35456.0 0.0 1048576.0 4992.0 5 2
|
參數列表及含義以下:
參數 |
參數含義 |
NGCMN |
年輕代的 初始化(最小)容量 |
NGCMX |
年輕代的 最大容量 |
NGC |
年輕代 當前的容量 |
S0C |
年輕代中 第一個 survivor 區的容量 |
S1C |
年輕代中 第二個 survivor 區的容量 |
EC |
年輕代中 Eden (伊甸園)的容量 |
OGCMN |
老年代中 初始化(最小)容量 |
OGCMX |
老年代的 最大容量 |
OGC |
老年代 當前新生成 的容量 |
OC |
老年代的容量大小 |
MCMN |
元空間 的 初始化容量 |
MCMX |
元空間 的 最大容量 |
MC |
元空間 當前 新生成 的容量 |
CCSMN |
最小 壓縮類空間大小 |
CCSMX |
最大 壓縮類空間大小 |
CCSC |
當前 壓縮類空間大小 |
YGC |
從應用程序啓動到採樣時 年輕代 中的 gc 次數 |
FGC |
從應用程序啓動到採樣時 老年代 中的 gc 次數 |
顯示 元空間(metaspace
)中 對象 的信息及其佔用量。
1 2 3
|
$ jstat -gcmetacapacity 8615 MCMN MCMX MC CCSMN CCSMX CCSC YGC FGC FGCT GCT 0.0 1079296.0 35456.0 0.0 1048576.0 4992.0 5 2 0.075 0.131
|
參數列表及含義以下:
參數 |
參數含義 |
MCMN |
最小 元數據空間容量 |
MCMX |
最大 元數據空間容量 |
MC |
當前 元數據空間容量 |
CCSMN |
最小壓縮 類空間容量 |
CCSMX |
最大壓縮 類空間容量 |
CCSC |
當前 壓縮類空間容量 |
YGC |
從應用程序啓動到採樣時 年輕代 中 gc 次數 |
FGC |
從應用程序啓動到採樣時 老年代 中 gc 次數 |
FGCT |
從應用程序啓動到採樣時 老年代 gc 所用時間 |
GCT |
從應用程序啓動到採樣時 gc 用的 總時間 |
2.6. gcnew
顯示 年輕代對象 的相關信息,包括兩個 survivor
區和 一個 Eden
區。
1 2 3
|
$ jstat -gcnew 8615 S0C S1C S0U S1U TTv MTT DSS EC EU YGC YGCT 20480.0 10752.0 0.0 0.0 6 15 20480.0 262144.0 131406.0 5 0.056
|
參數列表及含義以下:
參數 |
參數含義 |
S0C |
年輕代中第一個 survivor 的容量 |
S1C |
年輕代中第二個 survivor 的容量 |
S0U |
年輕代中第一個 survivor 目前已使用空間 |
S1U |
年輕代中第二個 survivor 目前已使用空間 |
TT |
持有次數限制 |
MTT |
最大持有次數限制 |
DSS |
指望的 倖存區 大小 |
EC |
年輕代中 Eden 的容量 |
EU |
年輕代中 Eden 目前已使用空間 |
YGC |
從應用程序啓動到採樣時 年輕代 中 gc 次數 |
YGCT |
從應用程序啓動到採樣時 年輕代 中 gc 所用時間 |
2.7. gcnewcapacity
查看 年輕代 對象的信息及其佔用量。
1 2 3
|
$ jstat -gcnewcapacity 8615 NGCMN NGCMX NGC S0CMX S0C S1CMX S1C ECMX EC YGC FGC 87040.0 1397760.0 372736.0 465920.0 20480.0 465920.0 10752.0 1396736.0 262144.0 5 2
|
參數列表及含義以下:
參數 |
參數含義 |
NGCMN |
年輕代中初始化(最小)的大小 |
NGCMX |
年輕代的最大容量 |
NGC |
年輕代中當前的容量 |
S0CMX |
年輕代中第一個 survivor 的最大容量 |
S0C |
年輕代中第一個 survivor 的容量 |
S1CMX |
年輕代中第二個 survivor 的最大容量 |
S1C |
年輕代中第二個 survivor 的容量 |
ECMX |
年輕代中 Eden 的最大容量 |
EC |
年輕代中 Eden 的容量 |
YGC |
從應用程序啓動到採樣時 年輕代 中 gc 次數 |
FGC |
從應用程序啓動到採樣時 老年代 中 gc 次數 |
2.8. gcold
顯示 老年代對象 的相關信息。
1 2 3
|
$ jstat -gcold 8615 MC MU CCSC CCSU OC OU YGC FGC FGCT GCT 35456.0 33931.0 4992.0 4582.0 165376.0 24093.7 5 2 0.075 0.131
|
參數列表及含義以下:
參數 |
參數含義 |
MC |
元空間(metaspace )的容量 |
MU |
元空間(metaspace )目前已使用空間 |
CCSC |
壓縮類空間大小 |
CCSU |
壓縮類空間 使用 大小 |
OC |
老年代 的容量 |
OU |
老年代 目前已使用空間 |
YGC |
從應用程序啓動到採樣時 年輕代 中 gc 次數 |
FGC |
從應用程序啓動到採樣時 老年代 中 gc 次數 |
FGCT |
從應用程序啓動到採樣時 老年代 gc 所用時間 |
GCT |
從應用程序啓動到採樣時 gc 用的 總時間 |
2.9. gcoldcapacity
查看 老年代 對象的信息及其佔用量。
1 2 3
|
$ jstat -gcoldcapacity 8615 OGCMN OGCMX OGC OC YGC FGC FGCT GCT 175104.0 2796544.0 165376.0 165376.0 5 2 0.075 0.131
|
參數列表及含義以下:
參數 |
參數含義 |
OGCMN |
老年代 中初始化(最小)的大小 |
OGCMX |
老年代 的最大容量 |
OGC |
老年代 當前新生成的容量 |
OC |
老年代 的容量 |
YGC |
從應用程序啓動到採樣時 年輕代 中 gc 的次數 |
FGC |
從應用程序啓動到採樣時 老年代 中 gc 的次數 |
FGCT |
從應用程序啓動到採樣時 老年代 中 gc 所用時間 |
GCT |
從應用程序啓動到採樣時 gc 用的 總時間 |
2.10. gcutil
顯示 垃圾回收(gc
)過程當中的信息,包括各個 內存的使用佔比,垃圾回收 時間 和回收 次數。
1 2 3
|
$ jstat -gcutil 8615 S0 S1 E O M CCS YGC YGCT FGC FGCT GCT 0.00 0.00 50.13 14.57 95.70 91.79 5 0.056 2 0.075 0.131
|
參數列表及含義以下:
參數 |
參數含義 |
S0 |
年輕代中 第一個 survivor 區 已使用 的佔當前容量百分比 |
S1 |
年輕代中 第二個 survivor 區 已使用 的佔當前容量百分比 |
E |
年輕代中 Eden 區 已使用 的佔當前容量百分比 |
O |
老年代 中 已使用 的佔當前容量百分比 |
M |
元空間(metaspace )中 已使用 的佔當前容量百分比 |
YGC |
從應用程序啓動到採樣時 年輕代 中 gc 次數 |
YGCT |
從應用程序啓動到採樣時 年輕代 中 gc 所用時間 |
FGC |
從應用程序啓動到採樣時 老年代 gc 次數 |
FGCT |
從應用程序啓動到採樣時 老年代 gc 所用時間 |
GCT |
從應用程序啓動到採樣時 gc 用的 總時間 |
3. jmap堆內存統計工具
jmap
(JVM Memory Map
) 命令用來查看 堆內存 使用情況,通常結合 jhat
使用,用於生成 heap dump
文件。jmap
不只能生成 dump
文件,還能夠查詢 finalize
執行隊列、Java
堆 和 元空間 metaspace
的詳細信息,如當前 使用率、當前使用的是哪一種 收集器 等等。
若是不使用這個命令,還可使用 -XX:+HeapDumpOnOutOfMemoryError
參數來讓虛擬機出現 OOM
的時候,自動生成 dump
文件。
命令格式以下:
1 2 3 4 5 6 7
|
Usage: 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)
|
參數含義以下:
- pid:本地
jvm
服務的進程 ID
;
- executable core:打印 堆棧跟蹤 的核心文件;
- remote server IP/hostname:遠程
debug
服務的 主機名 或 IP
地址;
- server id:遠程
debug
服務的 進程 ID
。
參數選項說明以下:
參數 |
參數含義 |
heap |
顯示 堆 中的摘要信息 |
histo |
顯示 堆 中對象的統計信息 |
histo[:live] |
只顯示 堆 中 存活對象 的統計信息 |
clstats |
顯示 類加載 的統計信息 |
finalizerinfo |
顯示在 F-Queue 隊列 等待 Finalizer 線程執行 finalizer 方法的對象 |
dump |
導出內存轉儲快照 |
注意:dump
內存快照分析基本上包含了 histo
、clstats
、finalizerinfo
等功能。
3.1. heap
顯示 堆 中的摘要信息。包括 堆內存 的使用狀況,正在使用的 GC
算法、堆配置參數 和 各代中堆內存 使用狀況。能夠用此來判斷內存目前的 使用狀況 以及 垃圾回收 狀況。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47
|
$ jmap -heap 11368 Attaching to process ID 11368, please wait... Debugger attached successfully. Server compiler detected. JVM version is 25.101-b13
using thread-local object allocation. Parallel GC with 2 thread(s)
Heap Configuration: MinHeapFreeRatio = 0 MaxHeapFreeRatio = 100 MaxHeapSize = 2684354560 (2560.0MB) NewSize = 1073741824 (1024.0MB) MaxNewSize = 1073741824 (1024.0MB) OldSize = 1610612736 (1536.0MB) 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 = 852492288 (813.0MB) used = 420427144 (400.95056915283203MB) free = 432065144 (412.04943084716797MB) 49.31741317993014% used From Space: capacity = 113770496 (108.5MB) used = 2299712 (2.19317626953125MB) free = 111470784 (106.30682373046875MB) 2.021360617079493% used To Space: capacity = 107479040 (102.5MB) used = 0 (0.0MB) free = 107479040 (102.5MB) 0.0% used PS Old Generation capacity = 1610612736 (1536.0MB) used = 50883368 (48.526161193847656MB) free = 1559729368 (1487.4738388061523MB) 3.1592552860577903% used
27595 interned Strings occupying 3138384 bytes.
|
這裏主要對 heap configuration
的參數列表說明一下:
參數 |
對應啓動參數 |
參數含義 |
MinHeapFreeRatio |
-XX:MinHeapFreeRatio |
JVM堆最小空閒比率(default 40) |
MaxHeapFreeRatio |
-XX:MaxHeapFreeRatio |
JVM堆最大空閒比率(default 70) |
MaxHeapSize |
XX:Xmx |
JVM堆的最大大小 |
NewSize |
-XX:NewSize |
JVM堆新生代的默認(初始化)大小 |
MaxNewSize |
-XX:MaxNewSize |
JVM堆新生代的最大大小 |
OldSize |
-XX:OldSize |
JVM堆老年代的默認(初始化)大小 |
NewRatio |
-XX:NewRatio |
JVM堆新生代和老年代的大小比例 |
SurvivorRatio |
-XX:SurvivorRatio |
JVM堆年輕代中Eden區與Survivor區的大小比值 |
MetaspaceSize |
-XX:MetaspaceSize |
JVM元空間(metaspace)初始化大小 |
MaxMetaspaceSize |
-XX:MaxMetaspaceSize |
JVM元空間(metaspace)最大大小 |
CompressedClass SpaceSize |
-XX:CompressedClass SpaceSize |
JVM類指針壓縮空間大小, 默認爲1G |
G1HeapRegionSize |
-XX:G1HeapRegionSize |
使用G1垃圾回收器時單個Region的大小,取值爲1M至32M |
3.2. histo
打印堆的 對象統計,包括 對象實例數、內存大小 等等。由於在 histo:live
前會進行 full gc
,若是帶上 live
則只統計 活對象。不加 live
的堆大小要大於加 live
堆的大小。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
|
$ jmap -histo:live 12498 num |
其中,class name
是 對象類型,對象 縮寫類型 與 真實類型 的對應說明以下:
對象縮寫類型 |
對象真實類型 |
B |
byte |
C |
char |
D |
double |
F |
float |
I |
int |
J |
long |
Z |
boolean |
[ |
數組,如[I表示int[] |
[L+類名 |
其餘對象 |
3.3. dump
dump
用於導出內存轉儲快照。經常使用的方式是經過 jmap
把進程 內存使用狀況 dump
到文件中,再用 jhat
分析查看。jmap
進行 dump
的命令格式以下:
1
|
jmap -dump:format=b,file=dumpFileName
|
參數含義以下:
參數 |
參數含義 |
dump |
堆到文件 |
format |
指定輸出格式 |
live |
指明是活着的對象 |
file |
指定文件名 |
- 經過
jmap
導出 內存快照,文件命名爲 dump.dat
:
1 2 3
|
jmap -dump:format=b,file=dump.dat 12498 Dumping heap to /Users/XXX/dump.dat ... Heap dump file created
|
導出的 dump
文件能夠經過 MAT
、VisualVM
和 jhat
等工具查看分析,後面會詳細介紹。
4. jhat堆快照分析工具
jhat
(JVM Heap Analysis Tool
)命令一般與 jmap
搭配使用,用來分析 jmap
生成的 dump
。jhat
內置了一個微型的 HTTP/HTML
服務器,生成 dump
的分析結果後,能夠在瀏覽器中查看。
注意:通常不會直接在 服務器 上 進行分析,由於使用 jhat
是一個 耗時 而且 耗費硬件資源 的過程,通常的作法是,把 服務器 生成的 dump
文件複製到 本地 或 其餘機器 上進行分析。
命令格式以下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
|
Usage: jhat [-stack <bool>] [-refs <bool>] [-port <port>] [-baseline <file>] [-debug <int>] [-version] [-h|-help] <file>
-J<flag> Pass <flag> directly to the runtime system. For example, -J-mx512m to use a maximum heap size of 512MB -stack false: Turn off tracking object allocation call stack. -refs false: Turn off tracking of references to objects -port <port>: Set the port for the HTTP server. Defaults to 7000 -exclude <file>: Specify a file that lists data members that should be excluded from the reachableFrom query. -baseline <file>: Specify a baseline object dump. Objects in both heap dumps with the same ID and same class will be marked as not being "new". -debug <int>: Set debug level. 0: No debug output 1: Debug hprof file parsing 2: Debug hprof file parsing, no server -version Report version number -h|-help Print this help and exit <file> The file to read
|
參數含義以下:
參數 |
參數值默認值 |
參數含義 |
stack |
true |
關閉 對象分配調用棧跟蹤。若是分配位置信息在堆轉儲中不可用。則必須將此標誌設置爲false。 |
refs |
true |
關閉 對象引用跟蹤。默認狀況下,返回的指針是指向其餘特定對象的對象。如 反向連接 或 輸入引用,會統計/計算堆中的全部對象 |
port |
7000 |
設置jhat HTTP server的端口號 |
exclude |
— |
指定對象查詢時須要排除的數據成員列表文件 |
baseline |
— |
指定一個 基準堆轉儲。在兩個heap dumps中有相同object ID的對象時,會被標記爲不是新的,其餘對象被標記爲新的。在比較兩個不一樣的堆轉儲時頗有用 |
debug |
0 |
設置debug級別,0表示不輸出調試信息。值越大則表示輸出更詳細的debug信息 |
version |
— |
啓動後只顯示版本信息就退出 |
J |
— |
jhat命令實際上會啓動一個JVM來執行,經過-J能夠在啓動JVM時傳入一些 啓動參數。例如, -J-Xmx512m則指定運行jhat 的Java虛擬機使用的最大堆內存爲512MB。 |
- 前面提到,經過
jmap dump
出來的文件能夠用 MAT
、VisualVM
等工具查看,這裏咱們用 jhat
查看:
1 2 3 4 5 6 7 8 9 10
|
$ jhat -port 7000 dump.dat Reading from dump.dat... Dump file created Sun Aug 12 12:15:02 CST 2018 Snapshot read, resolving... Resolving 1788693 objects... Chasing references, expect 357 dots..................................................................................................................................................................................................................................................................................................................................................................... Eliminating duplicate references..................................................................................................................................................................................................................................................................................................................................................................... Snapshot resolved. Started HTTP server on port 7000 Server is ready.
|
- 打開瀏覽器,輸入
http://localhost:7000
,查看 jhat
的分析報表頁面:

- 能夠按照 包名稱 查看項目模塊中的具體 對象示例:

除此以外,報表分析的最後一頁,還提供了一些擴展查詢:

- 顯示全部的
Root
集合;
- 顯示全部
class
的當前 對象實例數量(包含 JVM
平臺相關類);
- 顯示全部
class
的當前 對象實例數量(除去 JVM
平臺相關類);
- 顯示 堆內存 中實例對象的 統計直方圖(和直接使用
jmap
沒有區別);
- 顯示
finalizer
虛擬機 二次回收 的信息摘要;
- 執行
jhat
提供的 對象查詢語言(OQL
)獲取指定對象的實例信息。
注意:jhat
支持根據某些條件來 過濾 或 查詢 堆的對象。能夠在 jhat
的 html
頁面中執行 OQL
語句,來查詢符合條件的對象。OQL
`具體的語法能夠直接訪問 http://localhost:7000/oqlhelp。
在具體排查時,須要結合代碼,觀察是否 大量應該被回收 的對象 一直被引用,或者是否有 佔用內存特別大 的對象 沒法被回收。
5. jstack堆棧跟蹤工具
jstack
用於生成 java
虛擬機當前時刻的 線程快照。線程快照 是當前 java
虛擬機內 每一條線程 正在執行的 方法堆棧 的 集合。生成線程快照的主要目的是定位線程出現 長時間停頓 的緣由,如 線程間死鎖、死循環、請求外部資源 致使的 長時間等待 等等。
線程出現 停頓 的時候,經過 jstack
來查看 各個線程 的 調用堆棧,就能夠知道沒有響應的線程到底在後臺作什麼事情,或者等待什麼資源。若是 java
程序 崩潰 生成 core
文件,jstack
工具能夠經過 core
文件獲取 java stack
和 native stack
的信息,從而定位程序崩潰的緣由。
命令格式以下:
1 2 3 4 5 6 7 8 9
|
Usage: jstack [-l] <pid> (to connect to running process) jstack -F [-m] [-l] <pid> (to connect to a hung process) jstack [-m] [-l] <executable> <core> (to connect to a core file) jstack [-m] [-l] [server_id@]<remote server IP or hostname> (to connect to a remote debug server)
|
參數含義以下:
- pid:本地
jvm
服務的進程 ID
;
- executable core:打印 堆棧跟蹤 的核心文件;
- remote server IP/hostname:遠程
debug
服務的 主機名 或 IP
地址;
- server id:遠程
debug
服務的 進程 ID
。
參數選項說明以下:
參數 |
參數含義 |
F |
當正常輸出請求 不被響應 時,強制輸出 線程堆棧 |
l |
除堆棧外,顯示關於 鎖的附加信息 |
m |
若是調用到 本地方法 的話,能夠顯示 C/C++ 的堆棧 |
注意:在實際運行中,每每一次 dump
的信息,還不足以確認問題。建議產生三次 dump
信息,若是每次 dump
都指向同一個問題,才能肯定問題的典型性。
5.1. 系統線程狀態
在 dump
文件裏,值得關注的 線程狀態 有:
- 死鎖:Deadlock(重點關注)
- 執行中:Runnable
- 等待資源:Waiting on condition(重點關注)
- 等待獲取監視器:Waiting on monitor entry(重點關注)
- 暫停:Suspended
- 對象等待中:Object.wait() 或 TIMED_WAITING
- 阻塞:Blocked(重點關注)
- 中止:Parked
具體的含義以下所示:
(a). Deadlock
死鎖線程,通常指多個線程調用期間發生 資源的相互佔用,致使一直等待沒法釋放的狀況。
(b). Runnable
通常指該線程正在 執行狀態 中,該線程佔用了 資源,正在 處理某個請求。有可能正在傳遞 SQL
到數據庫執行,有可能在對某個文件操做,有可能進行數據類型等轉換。
(c). Waiting on condition
該狀態在線程等待 某個條件 的發生。具體是什麼緣由,能夠結合 stacktrace
來分析。線程處於這種 等待狀態,一旦有數據準備好讀以後,線程會從新激活,讀取並處理數據。
線程正處於等待資源或等待某個條件的發生,具體的緣由須要結合下面堆棧信息進行分析。
-
若是 堆棧信息 明確是 應用代碼,則證實該線程正在 等待資源。通常是大量 讀取某種資源 且該資源採用了 資源鎖 的狀況下,線程進入 等待狀態。
-
若是發現有 大量的線程 都正處於這種狀態,而且堆棧信息中得知正在 等待網絡讀寫,這是由於 網絡阻塞 致使 線程沒法執行,頗有多是一個 網絡瓶頸 的徵兆:
- 網絡很是 繁忙,幾乎消耗了全部的帶寬,仍然有大量數據等待網絡讀寫;
- 網絡多是 空閒的,但因爲 路由 或 防火牆 等緣由,致使包沒法正常到達。
-
還有一種常見的狀況是該線程在 sleep
,等待 sleep
的時間到了,將被喚醒。
(d). Locked
線程阻塞,是指當前線程執行過程當中,所須要的資源 長時間等待 卻 一直未能獲取到,被容器的線程管理器標識爲 阻塞狀態,能夠理解爲 等待資源超時 的線程。
(e). Waiting for monitor entry 和 in Object.wait()
Monitor
是 Java
中實現線程之間的 互斥與協做 的主要手段,它能夠當作是 對象 或者 Class
的 鎖。每個對象都有一個 monitor
。
5.1. 死鎖示例
下面給出一個 死鎖 的案例,在 IntLock
中定義了兩個靜態的 可重入鎖 實例,在主方法中聲明瞭 兩個線程 對 兩把鎖 進行資源競爭。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54
|
public class DeadLockRunner { public static void main(String[] args) { IntLock r1 = new IntLock(1); IntLock r2 = new IntLock(2); Thread t1 = new Thread(r1); Thread t2 = new Thread(r2); t1.start(); t2.start(); }
public static class IntLock implements Runnable { private static ReentrantLock lock1 = new ReentrantLock(); private static ReentrantLock lock2 = new ReentrantLock(); private int lock;
public IntLock(int lock) { this.lock = lock; }
@Override public void run() { try { if (lock == 1) { lock1.lock();
try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); }
lock2.lock(); } else { lock2.lock();
try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); }
lock1.lock(); } } finally { if (lock1.isHeldByCurrentThread()) { lock1.unlock(); } if (lock2.isHeldByCurrentThread()) { lock2.unlock(); } } } } }
|
5.2. dump日誌分析
啓動 DeadLockRunner
的 main()
方法,使用 jps
查看阻塞的 jvm
進程的 id
,而後使用 jstack
查看 線程堆棧信息,能夠發現兩個線程相互 競爭資源,出現死鎖。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
|
$ jstack -l 15584 2018-08-12 20:35:40 Full thread dump Java HotSpot(TM) 64-Bit Server VM (25.172-b11 mixed mode):
// 省略...
Found one Java-level deadlock: ============================= "Thread-1": waiting for ownable synchronizer 0x000000076ad61180, (a java.util.concurrent.locks.ReentrantLock$NonfairSync), which is held by "Thread-0" "Thread-0": waiting for ownable synchronizer 0x000000076ad611b0, (a java.util.concurrent.locks.ReentrantLock$NonfairSync), which is held by "Thread-1"
Java stack information for the threads listed above: =================================================== "Thread-1": at sun.misc.Unsafe.park(Native Method) - parking to wait for <0x000000076ad61180> (a java.util.concurrent.locks.ReentrantLock$NonfairSync) at java.util.concurrent.locks.LockSupport.park(LockSupport.java:175) at java.util.concurrent.locks.AbstractQueuedSynchronizer.parkAndCheckInterrupt(AbstractQueuedSynchronizer.java:836) at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireQueued(AbstractQueuedSynchronizer.java:870) at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquire(AbstractQueuedSynchronizer.java:1199) at java.util.concurrent.locks.ReentrantLock$NonfairSync.lock(ReentrantLock.java:209) at java.util.concurrent.locks.ReentrantLock.lock(ReentrantLock.java:285) at io.ostenant.deadlock.DeadLockRunner$IntLock.run(DeadLockRunner.java:47) at java.lang.Thread.run(Thread.java:748) "Thread-0": at sun.misc.Unsafe.park(Native Method) - parking to wait for <0x000000076ad611b0> (a java.util.concurrent.locks.ReentrantLock$NonfairSync) at java.util.concurrent.locks.LockSupport.park(LockSupport.java:175) at java.util.concurrent.locks.AbstractQueuedSynchronizer.parkAndCheckInterrupt(AbstractQueuedSynchronizer.java:836) at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireQueued(AbstractQueuedSynchronizer.java:870) at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquire(AbstractQueuedSynchronizer.java:1199) at java.util.concurrent.locks.ReentrantLock$NonfairSync.lock(ReentrantLock.java:209) at java.util.concurrent.locks.ReentrantLock.lock(ReentrantLock.java:285) at io.ostenant.deadlock.DeadLockRunner$IntLock.run(DeadLockRunner.java:37) at java.lang.Thread.run(Thread.java:748)
Found 1 deadlock.
|
參考
周志明,深刻理解Java虛擬機:JVM高級特性與最佳實踐,機械工業出版社
歡迎關注技術公衆號:零壹技術棧
零壹技術棧
本賬號將持續分享後端技術乾貨,包括虛擬機基礎,多線程編程,高性能框架,異步、緩存和消息中間件,分佈式和微服務,架構學習和進階等學習資料和文章。