JVM常见参数和工具以及实战经验

25 阅读10分钟

JVM调优

1.调优参数

堆栈相关参数

参数含义说明
-Xms100M最小堆大小(初始化堆大小),默认为物理内存的1/64等同于-XX:InitialHeapSize=100M
-Xmn100M最大堆大小,默认为物理内存的1/4等同于-XX:MaxHeapSize=100M
-XX:NewSize=20M新生代最小值 
-XX:MaxNewSize=50M新生代最大值 
-Xmn50M新生代统一值相当于将同时设置了相同的NewSizeMaxNewSize
-XX:SurvivorRatio=8S区和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+UseAdptiveSizePolicyParallel 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:+PrintGCDateStampsGC日志中包含时间信息 
-Xloggc:filenameGC日志输出目录文件信息 
-XX:+PrintHeapAtGCGC时输出前后堆信息 
-Xlog:gc输出GC日志信息jdk11新增
-XX:+PrintTenuringDistributionMinor 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_heap_info.png

 

jcmd pid GC.class_histogram

  • 作用:查看每个类示例数量和占用大小
  • 备注:和jmap -histo pid效果一样

jcmd_class_histogram.png

 

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_vm_flags.png

 

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

jconsole_connect.png

注意:

  • 对于本地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"

 

使用介绍:

jcosole_detail.png

  • 概览:展示虚拟机运行数据的概要信息,包括堆内存使用量、线程、类、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。

因此,需自行下载。前往 visualvm.github.io/download.ht… 即可下载。其中:

  • zip文件:可适用于各种操作系统
  • dmg文件:可适用于macOS 建议下载zip文件。

下载完成后,解压,并按操作系统启动:

  • 对于非Windows系统:运行如下命令即可启动

    ${visualvm目录}/bin/visualvm
    
  • 对于Windows系统:执行如下文件即可。

    ${visualvm目录}/bin/visualvm.exe
    

 

使用:

visualvm.png

  • 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…

插件

分析堆Dump文件

  • File -Load - 选择hprof - 打开 - 分析

 

3.调优实战

问题1:CPU占用过高问题解决

可能原因:

  1. 无限while循环

常见解决方案:

  • 尽量避免,可以用信号量之后的唤醒
  • 让循环执行慢一点(例如Sleep)

 

  1. 频繁GC

解决方案:降低GC频率

  • 使用合适的垃圾收集器
  • 调整合适的堆栈大小

 

 

  1. 频繁创建爱新对象

解决方案:

  • 合理使用单例模式

 

  1. 序列化和反序列化

解决方案:

  • 合理使用API
  • 选择稳定流行的序列化组件

 

5.正则表达式

正则表达式字符串匹配引擎会发生回溯,不合理的表达式会导致多次回溯从而造成CPU过高

解决方案:

  • 修改表达式,降低回溯发生

 

6.频繁的线程上下文切换

解决方案:

方式1:top + jstack
  1. 先使用top指令查看各个进程cpu使用的情况,获取高进程的pid
  2. 使用 top -Hp pid查询所需进程pid的线程信息
  3. 将查询出的线程id从10进制转换为16进制
printf %f pid
  1. 借助jstack命令将指定线程堆栈信息dump到指定文件
jstack pid > xxx.txt
  1. 使用cat命令查看dump文件的指定线程的详细信息
cat xxx.txt | grep -A 30 8ccc
  • 8ccc是第三步转换为线程pid16进制
  • 该行指令作用是查看搜索xxx.txt中匹配到8ccc的30行文本

 

方式2:JMC
  • 需要开启允许远程连接配置

 

 

 

问题2:内存溢出

内存溢出的可能原因

  1. 堆内存溢出
  2. 栈内存溢出
  3. 方法区内存溢出
  4. 直接内存溢出

 

解决方案:

  1. 设置当发生OOM时生成dump文件:
-XX:+HeapDumpOnOutOfMemoryError

使用mat(也可以使用VisualVM)打开dump文件分析

 

 

 

问题3:栈溢出

问题原因

  • -Xss参数分配太少

 

如何同时运行更多的线程?

  1. 减少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