JVM调优
1.调优参数
堆栈相关参数
参数 | 含义 | 说明 |
---|---|---|
-Xms100M | 最小堆大小(初始化堆大小),默认为物理内存的1/64 | 等同于-XX:InitialHeapSize=100M |
-Xmn100M | 最大堆大小,默认为物理内存的1/4 | 等同于-XX:MaxHeapSize=100M |
-XX:NewSize=20M | 新生代最小值 | |
-XX:MaxNewSize=50M | 新生代最大值 | |
-Xmn50M | 新生代统一值 | 相当于将同时设置了相同的NewSize 和MaxNewSize |
-XX:SurvivorRatio=8 | S区和Eden区的比值 | 例如设置-XX:SurvivorRatio=8 相当于(S0+S1): Eden = 2:8 |
-XX:OldSize=64M | 设置老年代大小 | |
-XX:NewRatio=4 | 新生代:老年代的比值 | 例如设置-XX:NewRatio=4 相当于新生代比老年代为1:4 ,也就是新生代占整个堆内存的1/5 |
-XXPermSize=8m | 永久代最小值 | JDK1.8后失效 |
-XXMaxPermSize=8m | 永久代最大值 | JDK1.8后失效 |
-XX:MetaspaceSize=64m | 元空间初始值 | |
-XX:MaxMetaspaceSize=512m | 元空间最大值 | |
-Xss1024k | 线程栈内存大小 | 决定了函数调用的最大深度,超过时会出现 StackOverflowError ,默认512~1024kb |
-XX:MaxTenuringThreshold=6 | 提升年老代的最大临界值 | 默认值为15 |
注意:
- -Xms和-Xmn两个参数设置为相同值,防止内存漂移影响性能
- 默认元空间大小为物理内存大小
GC相关参数
参数 | 含义 | 说明 |
---|---|---|
-XX:ParllelGCThreads | 设置ParNew垃圾收集器的垃圾回收线程,默认CPU核心数 | |
-XX:MaxGCPauseMillis | 表示每次Parallel Scavenge GC的最大停顿毫秒数(尽量达到) | 太小的值会导致频繁GC(谨慎使用) |
-XX:GCTimeRatio | 表示希望Parallel Scavenge在GC花费不超过应用程序执行时间的1/(1+nnn) ,nnn为大于0小于100的整数。 | 此参数的值表示运行用户代码时间是GC运行时间的nnn倍。 |
-XX+UseAdptiveSizePolicy | Parallel Scavenge自适应GC调整策略 | 自适应权衡吞吐量和GC停顿时间,推荐 |
-XX:CMSInitiatingOccupancyFraction | 设置CMS老年代占比达到多少触发Full GC | 默认 68 |
-XX:UseCMSCompactAtFullCollection | 设置CMS在完成Full GC后是否要进行一次内存碎片整理 | 默认开启 |
-XX:CMSFullGCsBeforeCompaction | 进行几次Full GC后就进行一次内存碎片整理 | 默认0 |
-XX:G1HeapRegionSize | 指定Region大小,范围为1~32M | 要求为2次幂 |
-XX:MaxGCPauseMillis=xxx | 设置G1停顿毫秒数 | |
-XX:InitiatingHeapOccupancyPercent | 设置G1老年代触发Mixed GC的阈值 | 默认为45%,值越小越容易触发 |
-XX:ConcGCThreads | 设置并发阶段的线程数 | |
-XX:G1ReservePercent | 预留内存,防止晋升失败 | 默认为10%,取值为0~50 |
调试参数
参数 | 含义 | 说明 |
---|---|---|
-XX:+PrintCommandLineFlags | 打印出不是默认值的参数(被覆盖的) | |
-XX:+PrintGCDetails | 发生GC时打印详细信息 | jdk11后使用-Xlog:gc代替 |
-XX:+PrintGC | 发生GC时打印简易信息 | jdk11后使用-Xlog:gc代替 |
-XX:+PrintGCTimeStamps && -XX:+PrintGCDateStamps | GC日志中包含时间信息 | |
-Xloggc:filename | GC日志输出目录文件信息 | |
-XX:+PrintHeapAtGC | GC时输出前后堆信息 | |
-Xlog:gc | 输出GC日志信息 | jdk11新增 |
-XX:+PrintTenuringDistribution | Minor GC时显示Survivor区各个年龄段对象的大小 | |
-XX:+HeapDumpOnOutOfMemoryError | 发生OOM是转储heap.hprof快照文件 | 可以拿这个文件进一步分析OOM发生原因 |
-XX:HeapDumpPath=heap.hprof | 指定堆内存溢出打印目录 | 表示在当前目录生成一个 heap.hprof文件 |
2.调优工具
jcmd
用于将诊断命令请求发送到正在运行的Java虚拟机,从JDK 7开始提供,通过它我们可以抓现场堆栈(dump threads),抓运行时堆内存(dump heap)、采集GC日志、定位高CPU占用问题等
使用示例:
# 查看所有JVM进程
jcmd -l
# 打印指定进程上可用的性能计数器
jcmd 26089 PerfCounter.print
# 打印所有启动类为com.imooc.Application的应用上可用的性能计数器
jcmd com.imooc.Application PerfCounter.print
jcmd pid GC.heap_info
- 作用:打印堆内存信息
- 备注:包含Metaspance信息,比jstat命令更直观
jcmd pid GC.class_histogram
- 作用:查看每个类示例数量和占用大小
- 备注:和
jmap -histo pid
效果一样
jcmd pid Thread.print
- 作用:查看堆栈信息
- 备注:和
jstack -l
命令作用一样
jcmd pid GC.heap_dump FILE_NAME
- 作用:查看并导出heap dump文件,后续可以使用MAT和Visual VM等工具查看,只指定文件名的情况下默认生成在启动的JVM目录中
- 备注:和
jmap -dump:format=b,file=heapdump.phrof pid
作用一样
jcmd pid VM.flags
- 作用:查看JVM启动参数
jcmd pid VM.command_line
- 作用:查看JVM启动命令行参数
- 和
jinfo -flags pid
命令类似
jhat
jhat(
JVM Heap Analysis Tool
)用来分析jmap生成的堆Dump,jdk11已经被废弃,建议用其他更强的工具代替。
jinfo
主要用来查看与调整JVM参数
扩展:要想查看JVM参数,也可在启动时,指定 -XX:+PrintFlagsFinal
,这样会在启动时将JVM参数打印到日志。
jinfo -flags pid
- 作用:打印进程JVM参数
jmap
jmap全称
Java Memory Map
,用来展示对象内存映=或堆内存详细信息。
使用示例:
# 展示63120进程的类加载统计信息
jmap -clstats 63120
# 展示63120进程中等待finalization的对象的信息
jmap -finalizerinfo 63120
# 展示63120进程中堆的直方图
jmap -histo 63120
# 展示63120进程堆中存活对象的直方图
jmap -histo:live 63120
# Dump 63120这个进程中的存货对象的堆到dump.hprof文件
jmap -dump:live,format=b,file=dump.hprof 63120
jstack
jstack,全称
Stack Trace for Java
,用于打印当前虚拟机的线程快照(线程快照也叫Thread Dump或者javacore文件),可以用以排查死锁问题。
使用示例:
jstack 63120
jstack -l 63120
jconsole
JConsole(Java Monitoring and Management Console)是一款基于
JMX
(Java Management Extensions)的可视化监控、管理工具。它主要通过JMX的MBean(Managed Bean)对系统进行信息收集和参数动态调整。
启动:
jconsole
注意:
- 对于本地JVM进程,jconsole会自动搜索,无需用户人工指定;
-
对于远程连接,需要添加以下启动参数:
- -Dcom.sun.management.jmxremote 远程开启开关
- -Dcom.sun.management.jmxremote.port=1808 jmx远程调用端口
- -Dcom.sun.management.jmxremote.authenticate=false 不开启验证
- -Dcom.sun.management.jmxremote.ssl=false 不为ssl连接
- -Djava.rmi.server.hostname=34.126.141.21 服务器所在ip或者域名
对于直接jar命令启动的,输入以下形式的启动参数:
java -Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.port=1808 -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.ssl=false -Djava.rmi.server.hostname=34.126.141.21 -jar xxx.jar
对于封装成war包使用tomcat启动的在启动文件 catalina.sh
里面添加::
JAVA_OPTS="$JAVA_OPTS -Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.port=1808 -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.ssl=false -Djava.rmi.server.hostname=34.126.141.21"
使用介绍:
- 概览:展示虚拟机运行数据的概要信息,包括堆内存使用量、线程、类、CPU占用率的曲线图。这些曲线图本质上是内存、线程、类等几个页面的信息汇总。
- 内存:用于监控虚拟机内存的变化趋势,相当于可视化的jstat命令。
- 线程:监控应用线程的个数波动及状态,当遇到线程停顿的时候可以考虑用这个页面的功能进行分析,相当于可视化的jstack命令。
- 类:监控应用加载的类的变化趋势。
- VM概要:展示应用的一些概要信息。
- MBean:展示应用被JMX管理的Bean。
JMC
- 作为JMX控制台,监控虚拟机MBean提供的数据
- 可持续收集数据的JFR(Java Flight Recorder),并可作为JFR的可视化分析工具
对于JDK 10及更低版本,JDK内置了JMC,使用如下命令启动:
jmc
功能说明:
JMX
- 概览:各种概要信息
- MBean浏览器:展示应用被JMX管理的Bean
- 触发器:配置触发规则,当规则满足时,就触发某个操作(在操作一栏配置)
- 系统:查看系统相关信息
- 内存:查看内存相关信息
- 线程:查看线程相关信息
- 诊断命令:可视化使用诊断命令,相当于可视化的jcmd
JFR
- 自动分析结果:JMC自动给出的优化提议
- Java应用程序:展示应用的各种执行情况
- JVM内部:展示JVM层面的执行情况
- 环境:展示操作系统层面的执行情况
- 事件:展示录制期间发生的事件
VisualVM
官方说VisualVM是一个
All-in-One
Java Troubleshooting Tool,从JDK 6开始提供,是目前最强大的监控及故障处理程序之一。
启动:
JDK 8或更低版本
对于JDK 8及更低版本,JDK内置了VisualVM。只需执行如下命令即可启动
jvisualvm
JDK 9及更高版本
对于JDK 9及更高版本,VisualVM默认不再内置,而是作为一个独立项目维护,因此需要手动下载。独立下载的VisualVM,最低支持JDK 8。
- GitHub:github.com/oracle/visu…
- 官方网站:visualvm.github.io/
因此,需自行下载。前往 visualvm.github.io/download.ht… 即可下载。其中:
- zip文件:可适用于各种操作系统
- dmg文件:可适用于macOS 建议下载zip文件。
下载完成后,解压,并按操作系统启动:
-
对于非Windows系统:运行如下命令即可启动
${visualvm目录}/bin/visualvm
-
对于Windows系统:执行如下文件即可。
${visualvm目录}/bin/visualvm.exe
使用:
-
Overview:展示应用的概要信息,相当于可视化的jps、jinfo
-
Monitor:监控
- 图表:展示CPU、内存、类、线程等曲线图
- Perform GC:通知JVM执行垃圾回收
- Heap Dump:Dump堆,相当于jmap dump命令。点击后,在左侧的heapdump节点上右击另存为,可将其存储为文件。
-
Threads:查看线程状态
- Thread Dump:Dump线程,相当于jstack。点击后,在左侧的threaddump节点上右击另存为,可将其存储为文件。
-
Sampler:抽样器,可用于实时性能分析
- CPU抽样:可展示每个线程花费的CPU时间、分析热点方法等
- 内存抽样:展示堆直方图、每个线程的内存分配
-
Profiler:性能分析,提供了程序运行期方法级的处理器执行时间分析及内存分析
-
CPU性能分析、内存性能分析、JDBC性能分析
-
还可以配置想检查的范围。
-
注意点:
- 执行性能分析,==会对程序运行性能有比较大的影响==,一般不建议在生产环境使用这项功能。可在开发/测试环境去分析并调优,也可用JMC代替,JMC的性能分析能力更强,而且影响相对小很多
- 类共享(类共享是一种共享类,从而提升加载速度、节省内存的技术)可能会导致执行Profiler的应用崩溃,建议在执行Profiler的应用上添加
-Xshare:off
,关闭掉类共享。
-
扩展知识
OQL
OQL语法:blog.csdn.net/pange1991/a…
插件
- Tools - Plugins - 安装插件
- github.com/oracle/visu…
分析堆Dump文件
- File -Load - 选择hprof - 打开 - 分析
3.调优实战
问题1:CPU占用过高问题解决
可能原因:
- 无限while循环
常见解决方案:
- 尽量避免,可以用信号量之后的唤醒
- 让循环执行慢一点(例如Sleep)
- 频繁GC
解决方案:降低GC频率
- 使用合适的垃圾收集器
- 调整合适的堆栈大小
- 频繁创建爱新对象
解决方案:
- 合理使用单例模式
- 序列化和反序列化
解决方案:
- 合理使用API
- 选择稳定流行的序列化组件
5.正则表达式
正则表达式字符串匹配引擎会发生回溯,不合理的表达式会导致多次回溯从而造成CPU过高
解决方案:
- 修改表达式,降低回溯发生
6.频繁的线程上下文切换
解决方案:
方式1:top + jstack
- 先使用
top
指令查看各个进程cpu使用的情况,获取高进程的pid - 使用
top -Hp pid
查询所需进程pid的线程信息 - 将查询出的线程id从10进制转换为16进制
printf %f pid
- 借助
jstack
命令将指定线程堆栈信息dump到指定文件
jstack pid > xxx.txt
- 使用
cat
命令查看dump文件的指定线程的详细信息
cat xxx.txt | grep -A 30 8ccc
- 8ccc是第三步转换为线程pid16进制
- 该行指令作用是查看搜索xxx.txt中匹配到8ccc的30行文本
方式2:JMC
- 需要开启允许远程连接配置
问题2:内存溢出
内存溢出的可能原因
- 堆内存溢出
- 栈内存溢出
- 方法区内存溢出
- 直接内存溢出
解决方案:
- 设置当发生OOM时生成dump文件:
-XX:+HeapDumpOnOutOfMemoryError
使用mat
(也可以使用VisualVM)打开dump文件分析
问题3:栈溢出
问题原因
- -Xss参数分配太少
如何同时运行更多的线程?
- 减少Xss配置
让每个线程占用的内存减少
-Xss144k
2.增加栈能分配的内存总量
栈内存= 机器总内存 - 操作系统内存 - 堆内存 - 方法区内存 - 直接内存
-Xms=10m -Xmx=10m -XX:MetaspaceSize=10m
3.尽量杀死其他程序
4.修改操作系统对线程的限制
cat /proc/sys/kernel/threads-max
- 作用:系统支持的最大线程数,表示物理内存决定的理论系统进程数上限,一般很大
- 修改: sysctl -w kernel.threads-max=7726
=========================================================================================
cat /proc/sys/kernel/pid_max
- 作用:查看系统限制某用户下最多可以运行多少进程或线程
- 修改:sysctl -w kernel.pid_max=65535
=========================================================================================
cat /proc/sys/vm/max_map_count
- 作用:限制一个进程可以拥有的虚拟内存数量
- 修改:sysctl -w vm.max_map_count=262144
=========================================================================================
ulimit -u
- 作用:查看用户最多可启动的进程数量
- 修改:ulimit -u 65535