JVM监控工具及一次调优实战记录

jinfo

jinfo 是 JDK 自带的命令,可以用来查看正在运行的 java 应用程序的扩展参数,包括Java System属性和JVM命令行参数;也可以动态的修改正在运行的 JVM 一些参数。当系统崩溃时,jinfo可以从core文件里面知道崩溃的Java应用程序的配置信息。

jinfo [option] pid
  • 无参,输出全部的参数和系统属性。
  • -flag name 输出对应名称的参数。
  • -flag [+|-]name 开启或者关闭对应名称的参数。
  • -flag name=value 设定对应名称的参数。
  • -flags 输出全部的参数。
  • -sysprops 输出系统属性。

通过 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日志

GC日志对调优分析十分重要。

JVM调优的目的就是尽可能的减少GC的次数。

jinfo -flag +PrintGC 1984 //输出GC日志
jinfo -flag -PrintGC 1984 //关闭GC日志

jstat

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。
一般会选择调大元空间的内存大小,默认分配的太小了。

查看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的信息显示是有规律的,大致整理如下:

  • O代表老年代。
  • Y代表新生代。
  • E代表伊甸区。
  • S代表幸存者区。
  • M代表元空间。
  • C代表总量。
  • U代表使用量。
  • T代表时间。

例如:

  • S0C:新生代中第一个幸存者区的总容量。
  • S0U:新生代中第一个幸存者区的使用量。
  • YGC:Minor gc(轻GC)的次数。
  • YGCT:Minor gc(轻GC)的时间。
  • FGC:Full GC的次数。
  • FGCT:Full GC的时间。
  • GCT: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
  • NGC:当前新生代空间
  • EC:Eden区的总空间
  • OGC:当前老年代空间
  • MC:当前元空间大小
  • CCSC:当前压缩类空间大小

jmap

查看内存信息。

显示堆信息

$ 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

可以设置:堆内存溢出时,自动导出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

jstack

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日志。

输出GC日志

设置JVM参数,Idea中可以在 Edit Configurations>VM Options 中设置。

-Dfile.encoding=UTF-8
-XX:+PrintGCDetails
-XX:+PrintGCTimeStamps
-Xloggc:/Users/panchanghe/temp/gc.log //GC日志输出目录

分析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次数。

通过以上日志文件,可以看到项目启动过程中,发生了:

  • 3次Full GC
  • 21次Minor 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。

不调优的情况总结如下:

  • 吞吐量:97.446%
  • 平均停顿时间:30.4 ms
  • 最长停顿时间:250 ms
  • Minor GC次数:21
  • Full GC:3

轻度调优

增大元空间大小-XX:MetaspaceSize=64M。

-Dfile.encoding=UTF-8
-XX:+PrintGCDetails
-XX:+PrintGCTimeStamps
-XX:MetaspaceSize=64M
-Xloggc:/Users/panchanghe/temp/gc_1.log

重新启动项目,再次分析GC日志(为了节省篇幅,这里不展示)。

调优后情况总结如下:

  • 吞吐量:97.702%
  • 平均停顿时间:42.5 ms
  • 最长停顿时间:310 ms
  • Minor GC次数:15
  • Full GC:1

可以看到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

你可能感兴趣的:(Java)