jinfo 是 JDK 自带的命令,可以用来查看正在运行的 java 应用程序的扩展参数,包括Java System属性和JVM命令行参数;也可以动态的修改正在运行的 JVM 一些参数。当系统崩溃时,jinfo可以从core文件里面知道崩溃的Java应用程序的配置信息。
jinfo [option] pid
通过 jps 查看运行的java进程。
$ jps
1984 jar
2492 Jps
输出JVM参数。
$ jinfo -flags 1984
Attaching to process ID 1984, please wait...
Debugger attached successfully.
Client compiler detected.
JVM version is 25.211-b12
Non-default VM flags: -XX:InitialHeapSize=16777216 -XX:MaxHeapSize=268435456 -XX
:MaxNewSize=89456640 -XX:MinHeapDeltaBytes=131072 -XX:NewSize=5570560 -XX:OldSiz
e=11206656 -XX:+UseFastUnorderedTimeStamps -XX:-UseLargePagesIndividualAllocatio
n
参数解析:
-XX:InitialHeapSize=16777216 //初始化堆内存
-XX:MaxHeapSize=268435456 // 最大堆内存
-XX:MaxNewSize=89456640 // 新生代最大内存
-XX:MinHeapDeltaBytes=131072 // 最小增量内存
-XX:NewSize=5570560 // 新生代内存
-XX:OldSize=11206656 // 老年代内存
-XX:+UseFastUnorderedTimeStamps
-XX:-UseLargePagesIndividualAllocation
GC日志对调优分析十分重要。
JVM调优的目的就是尽可能的减少GC的次数。
jinfo -flag +PrintGC 1984 //输出GC日志
jinfo -flag -PrintGC 1984 //关闭GC日志
jstat命令可以查看堆内存各部分的使用量,以及加载类的数量。
显示可选项。
$ jstat -options
-class //显示ClassLoad的相关信息
-compiler //显示JIT编译的相关信息
-gc //显示和gc相关的堆信息
-gccapacity //显示各个代的容量以及使用情况
-gccause //显示垃圾回收的相关信息
-gcmetacapacity //显示metaspace的大小
-gcnew //显示新生代信息
-gcnewcapacity //显示新生代大小和使用情况
-gcold //显示老年代信息
-gcoldcapacity // 显示老年代的大小
-gcutil //显示垃圾收集信息
-printcompilation //输出JIT编译的方法信息
$ jstat -class 1984
Loaded Bytes Unloaded Bytes Time
489 526.9 0 0.0 0.04
加载了489个类,元空间占用了526.9字节。
当项目比较庞大时,加载类的数量会很多,元空间内存很可能会因为不够用而触发GC。
一般会选择调大元空间的内存大小,默认分配的太小了。
$ jstat -gc 1984
S0C S1C S0U S1U EC EU OC OU MC MU CCSC CCSU YGC YGCT FGC FGCT GCT
512.0 512.0 0.0 0.0 4416.0 1147.4 10944.0 0.0 2240.0 1.2 0.0 0.0 0 0.000 0 0.000 0.000
GC的信息显示是有规律的,大致整理如下:
例如:
$ jstat -gccapacity 1984
NGCMN NGCMX NGC S0C S1C EC OGCMN OGCMX OGC OC MCMN MCMX MC CCSMN CCSMX CCSC YGC FGC
5440.0 87360.0 5440.0 512.0 512.0 4416.0 10944.0 174784.0 10944.0 10944.0 0.0 4480.0 2240.0 0.0 0.0 0.0 0 0
查看内存信息。
$ jmap -heap 1984
Attaching to process ID 1984, please wait...
Debugger attached successfully.
Client compiler detected.
JVM version is 25.211-b12
using thread-local object allocation.
Mark Sweep Compact GC
Heap Configuration:
MinHeapFreeRatio = 40
MaxHeapFreeRatio = 70
MaxHeapSize = 268435456 (256.0MB)
NewSize = 5570560 (5.3125MB)
MaxNewSize = 89456640 (85.3125MB)
OldSize = 11206656 (10.6875MB)
NewRatio = 2
SurvivorRatio = 8
MetaspaceSize = 12582912 (12.0MB)
CompressedClassSpaceSize = 1073741824 (1024.0MB)
MaxMetaspaceSize = 4294901760 (4095.9375MB)
G1HeapRegionSize = 0 (0.0MB)
Heap Usage:
New Generation (Eden + 1 Survivor Space):
capacity = 5046272 (4.8125MB)
used = 1174888 (1.1204605102539062MB)
free = 3871384 (3.6920394897460938MB)
23.282296316964285% used
Eden Space:
capacity = 4521984 (4.3125MB)
used = 1174888 (1.1204605102539062MB)
free = 3347096 (3.1920394897460938MB)
25.981692991394926% used
From Space:
capacity = 524288 (0.5MB)
used = 0 (0.0MB)
free = 524288 (0.5MB)
0.0% used
To Space:
capacity = 524288 (0.5MB)
used = 0 (0.0MB)
free = 524288 (0.5MB)
0.0% used
tenured generation:
capacity = 11206656 (10.6875MB)
used = 0 (0.0MB)
free = 11206656 (10.6875MB)
0.0% used
1735 interned Strings occupying 136576 bytes.
可以设置:堆内存溢出时,自动导出dump文件,方便排查问题。
-Xms1m
-Xmx1m
-XX:+PrintGCDetails
-XX:+HeapDumpOnOutOfMemoryError
-XX:HeapDumpPath=/Users/panchanghe/temp/oom.dump
触发内存溢出。
public static void main(String[] args) throws InterruptedException {
List list = new ArrayList();
while (true) {
list.add(new Object());
}
}
运行异常后,会生成一个dump文件,可以使用Java自带的工具jvisualvm分析。
MAC的工具目录如下:
/System/Library/Frameworks/JavaVM.framework/Versions/A/Commands/jvisualvm
jdk自带的线程堆栈分析工具,使用该命令可以查看或导出 Java 应用程序中线程堆栈信息。
通过jstack可以分析线程的运行状况,例如:分析死锁。
public static void main(String[] args) throws InterruptedException {
String s1 = "1";
String s2 = "2";
Thread t1 = new Thread(){
@Override
public void run() {
while (true) {
synchronized (s1) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {}
synchronized (s2) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {}
}
}
}
}
};
Thread t2 = new Thread(){
@Override
public void run() {
while (true) {
synchronized (s2) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {}
synchronized (s1) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {}
}
}
}
}
};
t1.start();
t2.start();
}
$ jstack 22396
2019-10-19 14:53:05
Full thread dump Java HotSpot(TM) 64-Bit Server VM (25.191-b12 mixed mode):
"Attach Listener" #14 daemon prio=9 os_prio=31 tid=0x00007fec6690c800 nid=0x5803 waiting on condition [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"DestroyJavaVM" #13 prio=5 os_prio=31 tid=0x00007fec6608f800 nid=0xf03 waiting on condition [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"线程2" #12 prio=5 os_prio=31 tid=0x00007fec6690c000 nid=0xa803 waiting for monitor entry [0x000070000f332000]
java.lang.Thread.State: BLOCKED (on object monitor)
at com.ch.jinfo.JInfoDemo$2.run(JInfoDemo.java:42)
- waiting to lock <0x000000076ae47278> (a java.lang.String)
- locked <0x000000076ae472a8> (a java.lang.String)
"线程1" #11 prio=5 os_prio=31 tid=0x00007fec63831000 nid=0xa903 waiting for monitor entry [0x000070000f22f000]
java.lang.Thread.State: BLOCKED (on object monitor)
at com.ch.jinfo.JInfoDemo$1.run(JInfoDemo.java:25)
- waiting to lock <0x000000076ae472a8> (a java.lang.String)
- locked <0x000000076ae47278> (a java.lang.String)
"Service Thread" #10 daemon prio=9 os_prio=31 tid=0x00007fec6604c000 nid=0x4103 runnable [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"C1 CompilerThread3" #9 daemon prio=9 os_prio=31 tid=0x00007fec63815800 nid=0x3f03 runnable [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"C2 CompilerThread2" #8 daemon prio=9 os_prio=31 tid=0x00007fec63808000 nid=0x4403 waiting on condition [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"C2 CompilerThread1" #7 daemon prio=9 os_prio=31 tid=0x00007fec64950000 nid=0x3d03 waiting on condition [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"C2 CompilerThread0" #6 daemon prio=9 os_prio=31 tid=0x00007fec6700e000 nid=0x3c03 waiting on condition [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"Monitor Ctrl-Break" #5 daemon prio=5 os_prio=31 tid=0x00007fec6700b000 nid=0x3a03 runnable [0x000070000eb1a000]
java.lang.Thread.State: RUNNABLE
at java.net.SocketInputStream.socketRead0(Native Method)
at java.net.SocketInputStream.socketRead(SocketInputStream.java:116)
at java.net.SocketInputStream.read(SocketInputStream.java:171)
at java.net.SocketInputStream.read(SocketInputStream.java:141)
at sun.nio.cs.StreamDecoder.readBytes(StreamDecoder.java:284)
at sun.nio.cs.StreamDecoder.implRead(StreamDecoder.java:326)
at sun.nio.cs.StreamDecoder.read(StreamDecoder.java:178)
- locked <0x000000076af14e40> (a java.io.InputStreamReader)
at java.io.InputStreamReader.read(InputStreamReader.java:184)
at java.io.BufferedReader.fill(BufferedReader.java:161)
at java.io.BufferedReader.readLine(BufferedReader.java:324)
- locked <0x000000076af14e40> (a java.io.InputStreamReader)
at java.io.BufferedReader.readLine(BufferedReader.java:389)
at com.intellij.rt.execution.application.AppMainV2$1.run(AppMainV2.java:64)
"Signal Dispatcher" #4 daemon prio=9 os_prio=31 tid=0x00007fec64812000 nid=0x3903 runnable [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"Finalizer" #3 daemon prio=8 os_prio=31 tid=0x00007fec6480f000 nid=0x4e03 in Object.wait() [0x000070000e80e000]
java.lang.Thread.State: WAITING (on object monitor)
at java.lang.Object.wait(Native Method)
- waiting on <0x000000076ab08ed0> (a java.lang.ref.ReferenceQueue$Lock)
at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:144)
- locked <0x000000076ab08ed0> (a java.lang.ref.ReferenceQueue$Lock)
at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:165)
at java.lang.ref.Finalizer$FinalizerThread.run(Finalizer.java:216)
"Reference Handler" #2 daemon prio=10 os_prio=31 tid=0x00007fec6400c800 nid=0x4f03 in Object.wait() [0x000070000e70b000]
java.lang.Thread.State: WAITING (on object monitor)
at java.lang.Object.wait(Native Method)
- waiting on <0x000000076ab06bf8> (a java.lang.ref.Reference$Lock)
at java.lang.Object.wait(Object.java:502)
at java.lang.ref.Reference.tryHandlePending(Reference.java:191)
- locked <0x000000076ab06bf8> (a java.lang.ref.Reference$Lock)
at java.lang.ref.Reference$ReferenceHandler.run(Reference.java:153)
"VM Thread" os_prio=31 tid=0x00007fec6480c800 nid=0x5003 runnable
"GC task thread#0 (ParallelGC)" os_prio=31 tid=0x00007fec64804800 nid=0x2207 runnable
"GC task thread#1 (ParallelGC)" os_prio=31 tid=0x00007fec66004000 nid=0x1d03 runnable
"GC task thread#2 (ParallelGC)" os_prio=31 tid=0x00007fec66004800 nid=0x1f03 runnable
"GC task thread#3 (ParallelGC)" os_prio=31 tid=0x00007fec66005000 nid=0x2a03 runnable
"GC task thread#4 (ParallelGC)" os_prio=31 tid=0x00007fec66006000 nid=0x2b03 runnable
"GC task thread#5 (ParallelGC)" os_prio=31 tid=0x00007fec64805800 nid=0x5203 runnable
"GC task thread#6 (ParallelGC)" os_prio=31 tid=0x00007fec63807000 nid=0x2d03 runnable
"GC task thread#7 (ParallelGC)" os_prio=31 tid=0x00007fec64806000 nid=0x5103 runnable
"VM Periodic Task Thread" os_prio=31 tid=0x00007fec63809000 nid=0x5503 waiting on condition
JNI global references: 15
Found one Java-level deadlock:
=============================
"线程2":
waiting to lock monitor 0x00007fec6580f8a8 (object 0x000000076ae47278, a java.lang.String),
which is held by "线程1"
"线程1":
waiting to lock monitor 0x00007fec6580d018 (object 0x000000076ae472a8, a java.lang.String),
which is held by "线程2"
Java stack information for the threads listed above:
===================================================
"线程2":
at com.ch.jinfo.JInfoDemo$2.run(JInfoDemo.java:42)
- waiting to lock <0x000000076ae47278> (a java.lang.String)
- locked <0x000000076ae472a8> (a java.lang.String)
"线程1":
at com.ch.jinfo.JInfoDemo$1.run(JInfoDemo.java:25)
- waiting to lock <0x000000076ae472a8> (a java.lang.String)
- locked <0x000000076ae47278> (a java.lang.String)
Found 1 deadlock.
注意看:Found one Java-level deadlock 下面。
栈信息中已经提示:发现一个Java级别的死锁。
线程名称:线程1、线程2。
并且还提示了代码的行号,我们可以根据这些信息去重新审视我们的代码。
记住一点:不要刻意的去调优。
JVM已经优化的很好了,如果项目运行的速度没问题,不要刻意的去调优,有可能越调越差。
如果需要调优,首先肯定是分析GC日志。
设置JVM参数,Idea中可以在 Edit Configurations>VM Options 中设置。
-Dfile.encoding=UTF-8
-XX:+PrintGCDetails
-XX:+PrintGCTimeStamps
-Xloggc:/Users/panchanghe/temp/gc.log //GC日志输出目录
推荐使用:https://gceasy.io。
在线分析GC日志,图形化界面显示更加直观。
会显示GC的停顿时间,吞吐量,GC次数等等,而且会给出优化建议(付费)。
Java HotSpot(TM) 64-Bit Server VM (25.191-b12) for bsd-amd64 JRE (1.8.0_191-b12), built on Oct 6 2018 08:37:07 by "java_re" with gcc 4.2.1 (Based on Apple Inc. build 5658) (LLVM build 2336.11.00)
Memory: 4k page, physical 16777216k(245892k free)
/proc/meminfo:
CommandLine flags: -XX:InitialHeapSize=268435456 -XX:+ManagementServer -XX:MaxHeapSize=4294967296 -XX:+PrintGC -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:+UseCompressedClassPointers -XX:+UseCompressedOops -XX:+UseParallelGC
2.129: [GC (Allocation Failure) [PSYoungGen: 65536K->8717K(76288K)] 65536K->8797K(251392K), 0.0085338 secs] [Times: user=0.03 sys=0.01, real=0.01 secs]
2.935: [GC (Allocation Failure) [PSYoungGen: 74253K->10726K(76288K)] 74333K->14628K(251392K), 0.0129900 secs] [Times: user=0.03 sys=0.01, real=0.01 secs]
3.813: [GC (Allocation Failure) [PSYoungGen: 76262K->10734K(76288K)] 80164K->15482K(251392K), 0.0072280 secs] [Times: user=0.03 sys=0.00, real=0.00 secs]
4.220: [GC (Allocation Failure) [PSYoungGen: 76270K->10733K(76288K)] 81018K->17334K(251392K), 0.0092126 secs] [Times: user=0.05 sys=0.00, real=0.01 secs]
4.467: [GC (Allocation Failure) [PSYoungGen: 76269K->10728K(76288K)] 82870K->18351K(251392K), 0.0075501 secs] [Times: user=0.03 sys=0.00, real=0.01 secs]
4.499: [GC (Metadata GC Threshold) [PSYoungGen: 17800K->10955K(145408K)] 25423K->18587K(320512K), 0.0106873 secs] [Times: user=0.04 sys=0.01, real=0.01 secs]
4.510: [Full GC (Metadata GC Threshold) [PSYoungGen: 10955K->0K(145408K)] [ParOldGen: 7631K->13346K(89088K)] 18587K->13346K(234496K), [Metaspace: 20604K->20604K(1069056K)], 0.0372467 secs] [Times: user=0.15 sys=0.00, real=0.04 secs]
5.552: [GC (Allocation Failure) [PSYoungGen: 131072K->6433K(145408K)] 144418K->19787K(234496K), 0.0065925 secs] [Times: user=0.02 sys=0.01, real=0.00 secs]
5.990: [GC (Allocation Failure) [PSYoungGen: 137505K->12728K(258048K)] 150859K->26090K(347136K), 0.0094178 secs] [Times: user=0.04 sys=0.01, real=0.01 secs]
7.130: [GC (Allocation Failure) [PSYoungGen: 257464K->15333K(260096K)] 270826K->48662K(349184K), 0.0438287 secs] [Times: user=0.14 sys=0.02, real=0.05 secs]
8.452: [GC (Allocation Failure) [PSYoungGen: 260069K->18882K(391168K)] 293398K->55084K(480256K), 0.0239632 secs] [Times: user=0.07 sys=0.01, real=0.02 secs]
9.815: [GC (Metadata GC Threshold) [PSYoungGen: 284250K->7942K(393216K)] 320451K->45263K(482304K), 0.0115916 secs] [Times: user=0.06 sys=0.01, real=0.01 secs]
9.826: [Full GC (Metadata GC Threshold) [PSYoungGen: 7942K->0K(393216K)] [ParOldGen: 37320K->24263K(120832K)] 45263K->24263K(514048K), [Metaspace: 34629K->34629K(1081344K)], 0.0472264 secs] [Times: user=0.23 sys=0.00, real=0.05 secs]
11.203: [GC (Allocation Failure) [PSYoungGen: 367616K->15092K(551936K)] 391879K->39363K(672768K), 0.0138760 secs] [Times: user=0.03 sys=0.00, real=0.01 secs]
12.899: [GC (Allocation Failure) [PSYoungGen: 551156K->25085K(561152K)] 575427K->51864K(681984K), 0.0234579 secs] [Times: user=0.09 sys=0.01, real=0.03 secs]
13.890: [GC (Allocation Failure) [PSYoungGen: 561149K->28647K(795136K)] 587928K->63415K(915968K), 0.0264850 secs] [Times: user=0.07 sys=0.02, real=0.03 secs]
16.297: [GC (Allocation Failure) [PSYoungGen: 795111K->13894K(801792K)] 829879K->49301K(922624K), 0.0191441 secs] [Times: user=0.06 sys=0.01, real=0.02 secs]
17.542: [GC (Allocation Failure) [PSYoungGen: 780358K->9904K(1092096K)] 815765K->49585K(1212928K), 0.0108911 secs] [Times: user=0.06 sys=0.01, real=0.01 secs]
19.390: [GC (Allocation Failure) [PSYoungGen: 1084592K->22448K(1108480K)] 1124273K->71898K(1229312K), 0.0346057 secs] [Times: user=0.13 sys=0.03, real=0.04 secs]
20.200: [GC (Metadata GC Threshold) [PSYoungGen: 599760K->11655K(1362432K)] 649211K->83581K(1483264K), 0.0371796 secs] [Times: user=0.16 sys=0.03, real=0.04 secs]
20.237: [Full GC (Metadata GC Threshold) [PSYoungGen: 11655K->0K(1362432K)] [ParOldGen: 71926K->72667K(196096K)] 83581K->72667K(1558528K), [Metaspace: 58429K->58429K(1101824K)], 0.2505378 secs] [Times: user=1.35 sys=0.03, real=0.25 secs]
23.848: [GC (Allocation Failure) [PSYoungGen: 1329664K->16534K(1362944K)] 1402331K->89210K(1559040K), 0.0236500 secs] [Times: user=0.09 sys=0.02, real=0.03 secs]
27.080: [GC (Allocation Failure) [PSYoungGen: 1346198K->11020K(1363968K)] 1418874K->95068K(1560064K), 0.0188131 secs] [Times: user=0.08 sys=0.00, real=0.02 secs]
30.708: [GC (Allocation Failure) [PSYoungGen: 1343244K->20251K(1363456K)] 1427292K->113563K(1559552K), 0.0222918 secs] [Times: user=0.11 sys=0.01, real=0.02 secs]
主要是看 Full GC,Full GC是非常消耗资源的。
调优的目的就是尽可能的减少GC次数。
通过以上日志文件,可以看到项目启动过程中,发生了:
重点看下面几行日志:
4.510: [Full GC (Metadata GC Threshold) [PSYoungGen: 10955K->0K(145408K)] [ParOldGen: 7631K->13346K(89088K)] 18587K->13346K(234496K), [Metaspace: 20604K->20604K(1069056K)], 0.0372467 secs] [Times: user=0.15 sys=0.00, real=0.04 secs]
9.826: [Full GC (Metadata GC Threshold) [PSYoungGen: 7942K->0K(393216K)] [ParOldGen: 37320K->24263K(120832K)] 45263K->24263K(514048K), [Metaspace: 34629K->34629K(1081344K)], 0.0472264 secs] [Times: user=0.23 sys=0.00, real=0.05 secs]
20.237: [Full GC (Metadata GC Threshold) [PSYoungGen: 11655K->0K(1362432K)] [ParOldGen: 71926K->72667K(196096K)] 83581K->72667K(1558528K), [Metaspace: 58429K->58429K(1101824K)], 0.2505378 secs] [Times: user=1.35 sys=0.03, real=0.25 secs]
3次Full GC的原因均是:Metadata GC Threshold,元空间内存不足。
原因是项目中含有大量的Controller、Service、Dao。
项目启动时,Spring需要扫描这些类,并实例化到IOC容器中。
MetaspaceSize默认分配大约20m左右,完全不够用,可以适当调大一点,毕竟物理内存现在动辄十几GB。
不调优的情况总结如下:
增大元空间大小-XX:MetaspaceSize=64M。
-Dfile.encoding=UTF-8
-XX:+PrintGCDetails
-XX:+PrintGCTimeStamps
-XX:MetaspaceSize=64M
-Xloggc:/Users/panchanghe/temp/gc_1.log
重新启动项目,再次分析GC日志(为了节省篇幅,这里不展示)。
调优后情况总结如下:
可以看到GC的次数减少了很多,吞吐量提升了一点点,停顿时间反而延长了,这和电脑的运行环境也有关系。
不要为了调优而调优,只在系统运行缓慢时进行必要的调优。
通过分析GC日志去针对性的调优测试。
不需要记住所有的调优参数,在需要的时候分析日志,然后进行必要的调优。
-Xss:每个线程的栈大小
-Xms:初始堆大小,默认物理内存的1/64
-Xmx:最大堆大小,默认物理内存的1/4
-Xmn:新生代大小
-XX:NewSize:设置新生代初始大小
-XX:NewRatio:默认2表示新生代占年老代的1/2,占整个堆内存的1/3。
-XX:SurvivorRatio:默认8表示一个survivor区占用1/8的Eden内存,即1/10的新生代内存。
-XX:MetaspaceSize:设置元空间大小
-XX:MaxMetaspaceSize:设置元空间最大允许大小,默认不受限制,JVM Metaspace会进行动态扩展。
-XX:+PrintGC
-XX:+PrintGCDetails
-XX:+PrintGCTimeStamps
-Xloggc:filename