JVM系列之性能调优参考手册(企业级实践篇)

338 阅读8分钟

1、前言介绍

一般来说,JVM内部是经过很多的实践和优化的,所以一般调休是先通过java提供的工具进行性能问题排查,找出影响性能的代码,然后迫不得已才可以考虑调整JVM参数,进行JVM参数调优。

2、标准参数

标准参数以-开头,所有jvm都实现了该参数的功能

-help
查看java命令帮助信息

-server -client
两个参数用于设置虚拟机使用何种运行模式,client模式启动比较快,但运行时性能和内存管理效率不如server模式。相反,server模式启动比client慢,但是运行时性能和内存管理效率比较高。64bit版本默认设置-server

-version
查看java版本,
-cp -classpath
指定classpath路径

-jar
指定运行一个jar包,jar包中manifest文件中必须指定Main-class

-verbose
打印jvm载入类的相关信息

-verbose:gc
打印gc信息

-verbose:jni
打印native方法调用信息

等等,…

3、非标准参数

参考资料:sourl.cn/sx6zLt

非标准参数以-X开头,给jvm实现这些参数的功能,但是不是所有的jvm都有这些功能。同时在不同JDK版本也有可能变动,所以说这些参数是非标准参数

-Xint
解释执行
-Xcomp
编译执行,第1次使用就编译成本地代码
-Xmixed
混合模式,jvm自己决定
[www@localhost ~]$ java -Xint -version
java version "11.0.9" 2020-10-20 LTS
Java(TM) SE Runtime Environment 18.9 (build 11.0.9+7-LTS)
Java HotSpot(TM) 64-Bit Server VM 18.9 (build 11.0.9+7-LTS, interpreted mode)

[www@localhost ~]$ java -Xcomp -version
java version "11.0.9" 2020-10-20 LTS
Java(TM) SE Runtime Environment 18.9 (build 11.0.9+7-LTS)
Java HotSpot(TM) 64-Bit Server VM 18.9 (build 11.0.9+7-LTS, compiled mode)

[www@localhost ~] java -Xmixed -version java version "11.0.9" 2020-10-20 LTS Java(TM) SE Runtime Environment 18.9 (build 11.0.9+7-LTS) Java HotSpot(TM) 64-Bit Server VM 18.9 (build 11.0.9+7-LTS, mixed mode) \[www@localhost ~\]

-Xms
eg:-Xms1g指定jvm初始内存为1g

-Xmx
eg:-Xmx1g指定jvm最大可用内存为1g

-Xmn
eg:-Xmn1g指定年轻代大小为1g,增加年轻代容量会缩小年老代容量

-Xss
-Xss1m,设置线程堆栈大小,默认1m

-Xloggc
eg:-Xloggc:gc.log,将gc日志记录到gc.log文件中

-Xprof
跟踪正运行的程序,并将跟踪数据在标准输出输出

等等,…

4、不稳定参数
no Stable,不稳定参数以 -XX开头,使用最多的参数类型,非标准化参数,相对不稳定,主要用于JVM调优和Debug

Boolean类型
格式:-XX[+/-] ,+ 表示启用,-表示禁用
例子:-XX:+UseConcMarkSweepGC,表示启用CMS类型的垃圾回收器
非Boolean类型
格式:-XX=表示name属性的值是value
例子:-XX:MaxGCPauseMillis=500
-XX:PermSize
非堆,也可以说是方法区,内存初始大小
-XX:MaxPermSize
方法区,最大内存大小
-XX:-UseSerialGC
使用串行GC
-XX:-UseParallelGC
使用并行GC
-XX:-UseConcMarkSweepGC
对老年代使用CMS(Concurrent Mark And Sweep)GC
参数 含义 说明
-XX:CICompilerCount 最大并行编译数 如果设置大于1,会提高编译速度,但是同样影响系统稳定性,会增加jvm奔溃的可能
-XX:InitialHeapSize=100M 初始化堆大小 简写-Xms100M
-XX:MaxHeapSize=100M 最大堆大小 简写-Xms100M
-XX:NewSize=20M 设置年轻代的大小
-XX:OldSize=50M 设置老年代的大小
-XX:MetaspaceSize=50M 设置方法区的大小
-XX:MaxMetaspaceSize=50M 方法区最大大小
-XX:+UseParallelGC 使用UseParallelGC 并行收集器,作用于新生代,吞吐量优先
-XX:+UseParallelOldGC 使用UseParallelOldGC 并行收集器,作用于老年代,吞吐量优先
-XX:+UseConcMarkSweepGC 使用CMS 作用于老年代,停顿时间优先
-XX:+UseG1GC 使用G1GC 作用于新生代,老年代,停顿时间优先
-XX:NewRatio 新老生代的比值 比如-XX:Ratio=4,表示新生代:老年代=1:4,也就是新生代占整个堆内存的1/5
-XX:SurvivorRatio 两个S区和Eden区的比值 比如-XX:SurvivorRatio=8,也就是(S0+S1):Eden=2:8,也就是一个S占整个新生代的1/10
-XX:+HeapDumpOnOutOfMemoryError 启动堆内存溢出打印 当JVM堆内存发生溢出时,也就是OOM,自动生成dump文件
-XX:HeapDumpPath=heap.hprof 指定堆内存溢出打印目录 表示在当前目录生成一个heap.hprof文件
-XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:+PrintGCDateStamps -Xloggc:gc.log 打印GC日志,保存到gc.log
-Xss128k 设置每个线程的堆栈大小 经验值是3000-5000最佳
-XX:MaxTenuringThreshold=15 提升年老代的最大临界值 默认值为 15
-XX:InitiatingHeapOccupancyPercent 启动并发GC周期时堆内存使用占比 默认值为 45.
-XX:G1HeapWastePercent 允许的浪费堆空间的占比 默认是10%,如果并发标记可回收的空间小于10%,则不会触发MixedGC
-XX:MaxGCPauseMillis=200M G1最大停顿时间 暂停时间不能太小,太小的话就会导致出现G1跟不上垃圾产生的速度。从而造成full gc。
-XX:ConcGCThreads=n 并发垃圾收集器使用的线程数量 默认值随JVM运行的平台不同而不同
-XX:G1MixedGCLiveThresholdPercent=65 混合垃圾回收周期中要包括的旧区域设置占用率阈值 默占用率为 65%
-XX:G1MixedGCCountTarget=8 设置标记周期完成后,对存活数据上限为G1MixedGCLIveThresholdPercent的旧区域执行混合垃圾回收的目标次数 默认8次混合垃圾回收,混合回收的目标是要控制在此目标次数以内
-XX:G1OldCSetRegionThresholdPercent=1 描述Mixed GC时,Old Region被加入到CSet中 默认占用率为 65%
-XX:LargePageSizeInBytes=4m 设置用于Java堆的大页面尺寸
-XX:MaxHeapFreeRatio=70 GC后java堆中空闲量占的最大比例
-XX:MaxNewSize=size 新生成对象能占用内存的最大值
-XX:MaxPermSize=64m 老生代对象能占用内存的最大值
-XX:MinHeapFreeRatio=40 GC后java堆中空闲量占的最小比例
-XX:ReservedCodeCacheSize=32m 保留代码占用的内存容量
-XX:ThreadStackSize=512 设置线程栈大小,若为0则使用系统默认值
-XX:+UseLargePages 使用大页面内存
-XX:-CITime 打印消耗在JIT编译的时间
-XX:ErrorFile=./hs_err_pid.log 保存错误日志或者数据到文件中
-XX:-ExtendedDTraceProbes 开启solaris特有的dtrace探针
-XX:OnError=”;“ 出现致命ERROR之后运行自定义命令
-XX:OnOutOfMemoryError=”;“ 当首次遭遇OOM时执行自定义命令
-XX:-PrintClassHistogram 遇到Ctrl-Break后打印类实例的柱状信息,与jmap -histo功能相同
-XX:-PrintConcurrentLocks 遇到Ctrl-Break后打印并发锁的相关信息,与jstack -l功能相同
-XX:-PrintCommandLineFlags 打印在命令行中出现过的标记
-XX:-PrintCompilation 当一个方法被编译时打印相关信息
-XX:-TraceClassLoading 跟踪类的加载信息
-XX:-TraceClassLoadingPreorder 跟踪被引用到的所有类的加载信息
-XX:-TraceClassResolution 跟踪常量池
-XX:-TraceClassUnloading 跟踪类的卸载信息
-XX:-TraceLoaderConstraints 跟踪类加载器约束的相关信息
[www@localhost ~]$ java -XX:+PrintFlagsFinal -version > log.txt
java version "1.8.0_31"
Java(TM) SE Runtime Environment (build 1.8.0_31-b13)
Java HotSpot(TM) 64-Bit Server VM (build 25.31-b07, mixed mode)

4
5、常用命令
jps
jps命令用于查看java进程

The jps command lists the instrumented Java HotSpot VMs on the target system. The
command is limited to reporting information on JVMs for which it has the access
permissions.

[www@localhost ~] jps 4352 8480 Jps 1736 RemoteMavenServer 3144 SmartGit 7612 Bootstrap \[www@localhost ~\] jps -l
3024 sun.tools.jps.Jps
4352
1736 org.jetbrains.idea.maven.server.RemoteMavenServer
3144 SmartGit
7612 org.apache.catalina.startup.Bootstrap

jinfo
jinfo命令用于实时查看和调整jvm配置参数
语法:

jinfo -flag name PID // 查看某个java进程的name属性的值
jinfo -flag [+/-] PID // 启用禁用JVM属性
jinfo -flag = PID // 设置JVM参数(name)的值(value)
查看进程4352的MaxHeapSize属性

[www@localhost ~]$ jinfo -flag MaxHeapSize 4352
-XX:MaxHeapSize=786432000
1
2
jinfo修改MaxHeapSize值

jinfo -flag MaxHeapSize=786432000 4352
1
jinfo -flag PID查看曾经赋过值的一些参数

[www@localhost ~]$ jinfo -flag 7612
Usage:
jinfo [option]
(to connect to running process)
jinfo [option] <executable
(to connect to a core file)
jinfo [option] [server_id@]
(to connect to remote debug server)

where is one of:
-flag to print the value of the named VM flag
-flag [+|-] to enable or disable the named VM flag
-flag = to set the named VM flag to the given value
-flags to print VM flags
-sysprops to print Java system properties
to print both of the above
-h | -help to print this help message

jstat
查看虚拟机性能统计信息
The jstat command displays performance statistics for an instrumented Java
HotSpot VM. The target JVM is identified by its virtual machine identifier, or
vmid option.

查看类装载信息,例子,查看某个java进程的类装载信息,每1000毫秒输出一次,共输出10次

[www@localhost ~]$ jstat -class 3760 1000 10
Loaded Bytes Unloaded Bytes Time
13080 26809.1 250 390.1 29.27
13080 26809.1 250 390.1 29.27
13080 26809.1 250 390.1 29.27
13080 26809.1 250 390.1 29.27
13080 26809.1 250 390.1 29.27
13080 26809.1 250 390.1 29.27
13080 26809.1 250 390.1 29.27
13080 26809.1 250 390.1 29.27
13080 26809.1 250 390.1 29.27
13080 26809.1 250 390.1 29.27

1
查看垃圾收集信息

[www@localhost ~]$ jstat -gc 3760 1000 10
S0C S1C S0U S1U EC EU OC OU MC MU
CCSC CCSU YGC YGCT FGC FGCT GCT
24576.0 48640.0 767.1 0.0 528896.0 492086.2 214528.0 109639.1 89856.0 87
215.8 9472.0 8955.5 28 0.965 4 0.810 1.775
24576.0 48640.0 767.1 0.0 528896.0 492086.2 214528.0 109639.1 89856.0 87
215.8 9472.0 8955.5 28 0.965 4 0.810 1.775
24576.0 48640.0 767.1 0.0 528896.0 492086.2 214528.0 109639.1 89856.0 87
215.8 9472.0 8955.5 28 0.965 4 0.810 1.775
24576.0 48640.0 767.1 0.0 528896.0 492086.2 214528.0 109639.1 89856.0 87
215.8 9472.0 8955.5 28 0.965 4 0.810 1.775
24576.0 48640.0 767.1 0.0 528896.0 492086.2 214528.0 109639.1 89856.0 87
215.8 9472.0 8955.5 28 0.965 4 0.810 1.775
24576.0 48640.0 767.1 0.0 528896.0 492086.2 214528.0 109639.1 89856.0 87
215.8 9472.0 8955.5 28 0.965 4 0.810 1.775
24576.0 48640.0 767.1 0.0 528896.0 492086.2 214528.0 109639.1 89856.0 87
215.8 9472.0 8955.5 28 0.965 4 0.810 1.775
24576.0 48640.0 767.1 0.0 528896.0 502965.7 214528.0 109639.1 89856.0 87
215.8 9472.0 8955.5 28 0.965 4 0.810 1.775
24576.0 48640.0 767.1 0.0 528896.0 502965.7 214528.0 109639.1 89856.0 87
215.8 9472.0 8955.5 28 0.965 4 0.810 1.775
24576.0 48640.0 767.1 0.0 528896.0 502965.7 214528.0 109639.1 89856.0 87
215.8 9472.0 8955.5 28 0.965 4 0.810 1.775

1

24
jstack
查看线程堆栈信息
The jstack command prints Java stack traces of Java threads for a specified Java
process, core file, or remote debug server.

语法:jstack PID

两个锁互相持有,造成死锁案例:

public class LockDemo
{
public static void main(String[] args)
{
LockThread d1=new LockThread(true);
LockThread d2=new LockThread(false);
Thread t1=new Thread(d1);
Thread t2=new Thread(d2);
t1.start();
t2.start();
}
}
//定义锁对象
class MyLock{
public static Object obj1=new Object();
public static Object obj2=new Object();
}
// 案例,thread1持有u1的锁,thread2持有u2的锁,thread1等待获取u2的锁,thread2等待获取u1的锁
class LockThread implements Runnable{
private boolean flag;
LockThread(boolean flag){
this.flag=flag;
}
public void run() {
if(flag) {
while(true) {
synchronized(MyLock.obj1) {
System.out.println(Thread.currentThread().getName()+"获得obj1锁");
synchronized(MyLock.obj2) {
System.out.println(Thread.currentThread().getName()+"获得obj2锁");
}
}
}
} else {
while(true){
synchronized(MyLock.obj2) {
System.out.println(Thread.currentThread().getName()+"获得obj2锁");
synchronized(MyLock.obj1) {
System.out.println(Thread.currentThread().getName()+"获得obj1锁");
}
}
}
}
}

}
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
2
jstack进行分析,

[www@localhost ~]$ jps -l
2560
2624 SmartGit
3760 org.apache.catalina.startup.Bootstrap
5316 org.jetbrains.idea.maven.server.RemoteMavenServer
7400 sun.tools.jps.Jps
8104 LockDemo
1116 org.jetbrains.jps.cmdline.Launcher

[www@localhost ~]$ jstack 8104

1
2
3
4
5
6
7
8
9
10
11

jmap
生成堆转储快照
The jmap command prints shared object memory maps or heap memory details of a
specified process, core file, or remote debug server.

语法:

jmap -heap PID
1

dump出堆内存相关信息

jmap -dump:format=b,file=heap.hprof PID
1

当然可以设置发生堆内存溢出的时候,能自动dump出该文件,加上启动参数:

-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=heap.hprof
1
写个Threadlocal例子:

import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping(value = {"/api"})
public class ThreadLocalHeapController {
// ThreadLocal 内存溢出例子
@GetMapping(value = "/testHeap")
public ResponseEntity<?> testHeap() {
ThreadLocal<Byte[]> threadLocal = new ThreadLocal<Byte[]>();
try {
threadLocal.set(new Byte[1024 * 1024]);
return ResponseEntity.ok("success");
}catch (Exception e) {
return ResponseEntity.badRequest().build();
} finally {
// 不进行ThreadLocal remove会出现内存溢出
//threadLocal.remove();
}
}

}

1
2
3

25
设置发生堆内存溢出的时候,能自动dump出日志文件

java -jar -Xms1000M -Xmx1000M -XX:+HeapDumpOnOutOfMemoryError -
XX:HeapDumpPath=jvm.hprof jvm-exception-example-0.0.1-SNAPSHOT.jar
1
2
在linux系统进行AB压测:10000次请求,并发100

[www@localhost ~]abn10000c100http://127.0.0.1:8080/api/testHeapThisisApacheBench,Version2.3< ab -n 10000 -c 100 http://127.0.0.1:8080/api/testHeap This is ApacheBench, Version 2.3 <Revision: 1430300 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, www.zeustech.net/
Licensed to The Apache Software Foundation, www.apache.org/

Benchmarking 127.0.0.1 (be patient)
Completed 1000 requests
Completed 2000 requests
Completed 3000 requests
Completed 4000 requests
Completed 5000 requests
Completed 6000 requests
Completed 7000 requests
Completed 8000 requests
Completed 9000 requests
Completed 10000 requests
Finished 10000 requests

Server Software:
Server Hostname: 127.0.0.1
Server Port: 8080

Document Path: /api/testHeap
Document Length: 7 bytes

Concurrency Level: 100
Time taken for tests: 10.504 seconds
Complete requests: 10000
Failed requests: 0
Write errors: 0
Total transferred: 1390000 bytes
HTML transferred: 70000 bytes
Requests per second: 952.01 [#/sec] (mean)
Time per request: 105.041 [ms] (mean)
Time per request: 1.050 [ms] (mean, across all concurrent requests)
Transfer rate: 129.23 [Kbytes/sec] received

Connection Times (ms)
min mean[+/-sd] median max
Connect: 0 1 0.7 0 10
Processing: 7 104 43.1 100 1223
Waiting: 7 100 38.8 99 1213
Total: 7 104 43.1 101 1224
WARNING: The median and mean for the initial connection time are not within a normal deviation
These results are probably not that reliable.

Percentage of the requests served within a certain time (ms)
50% 101
66% 105
75% 108
80% 110
90% 119
95% 144
98% 185
99% 219
100% 1224 (longest request)

jmap -heap PID再打印一下堆信息,可以看到并发的情况,内存溢出

当然拿到堆异常日志后,可以使用GCViewer等等根据进行性能分析,本博客只是以一个简单的例子进行分析,生产环境特别复杂,需要多年的经验和很好的耐心