【JVM】GC调优(优化JVM参数)、性能调优

GC调优

GC调优的主要目标是避免由垃圾回收引起程序性能下降。

GC调优的核心指标

  1. 垃圾回收吞吐量:执行用户代码时间/(执行用户代码时间 + GC时间)
  2. 延迟:GC延迟 + 业务执行时间
  3. 内存使用量

GC调优步骤

  1. 发现问题:通过监控工具。
  2. 诊断问题:通过分析工具。
  3. 修复问题:调整JVM参数或修复源代码。
  4. 测试验证

GC调优发现问题的工具

  1. jstat:jdk自带的可以提供垃圾回收的信息。 jstat -gc 进程ID 每次统计的间隔(毫秒) 统计次数。
  2. Visualvm插件:Visual GC插件,实时监控Java进程的堆内存结构、堆内存变化趋势以及垃圾回收时间的变化趋势。适合开发环境,对程序有影响,生产环境无权限。
  3. Prometheus + Grafana:非常详细,环境复杂,运维搭建。

分析GC日志

  1. GCViewer:日志转为可视化图表 java -jar gcviewer_1.3.4.jar 日志文件.log

https://github.com/chewiebug/GCViewer

  1. GCEasy:在线的可视化工具图表

https://gceasy.io/

常见的GC模式

  1. 正常情况
  2. 缓存对象过多
  3. 内存泄漏
  4. 持续的FullGC:请求量激增,生产更多对象,gc无法跟上对象创建速率。
  5. 元空间不足:堆中内存足够

GC调优的手段

  1. 优化基础JVM参数:基础JVM参数的设置不当,会导致频繁FULLGC的产生。
  2. 减少对象产生:大多数场景下的FULLGC是由于对象产生速度过快导致的。
  3. 更换垃圾回收器:选择适合当前业务场景的垃圾回收器,减少延迟、提高吞吐量。
  4. 优化垃圾回收器参数:

优化基础JVM参数

堆、栈、元空间、垃圾回收器、堆内存快照、日志

  1. -Xmx 和 --Xms 最大堆内存、初始堆内存。根据最大并发量估算服务器的配置,然后再减去系统和其他程序所需的内存。建议一样大,减少申请内存次数。
  2. -XX:MaxMetaspaceSize 和 -XX:MetaspaceSize 最大元空间大小、第一次FULLGC的阈值(之后JVM自行计算)。
  3. -Xss 虚拟机栈大小。默认大小取决于操作系统和体系结构。Linux x86 1m。合理值256k~1m。
  4. 不建议手动设置。
    1. -Xmn 年轻代的大小,默认值为整个堆的1/3。G1会动态调整年轻代大小。
    2. -XX:SurvivorRatio 伊甸园区和幸存者区的大小比例,默认值为8。
    3. -XX:MaxTenuringThreshold 最大晋升阈值,年龄大于此值,会进入老年代。JVM有动态年龄判断机制:将年龄从小到大的对象占据的空间加起来,如果大于survivor区域的50%,把等于或大于该年龄的对象,放入到老年代。
  5. 其他参数
    1. -XX:+DisableExplicitGC 禁止在代码中使用System.gc(),因为可能会引起FULLGC。
    2. -XX:+HeapDumpOnOutOfMemoryError 发生OOM错误时,自动生成hprof内存快照文件。
    3. -XX:HeapDumpPath= 指定hprof文件的输出路径。
    4. 打印GC日志:8及之前: -XX:+PrintGCDetails -XX:+PrintGCDateStamps -Xloggc:文件路径
    5. 打印GC日志:9及之后: -Xlog:gc*:file=文件路径

JVM参数模板

Java 复制代码
-Xms1g
-Xmx1g
-Xss256k
-XX:MaxMetaspaceSize=512m 
-XX:+DisableExplicitGC-XX:+HeapDumpOnOutOfMemoryError
-XX:HeapDumpPath=/opt/logs/my-service.hprof-XX:+PrintGCDetails 
-XX:+PrintGCDateStamps 
-Xloggc:文件路径

实战

保存hprof文件,Heap Hero分析文件

https://heaphero.io/

性能调优

问题:top命令CPU占用率高;单个服务处理时间特别长;程序启动后正常,一段时间后无法处理任何请求。

线程转储

线程转储(Thread Dump)提供了对所有运行中的线程当前状态的快照。

bash 复制代码
jstack 进程id
jstack 进程id > 文件名  # 导出线程栈文件

线程转储的可视化在线分析平台: 1、 https://jstack.review/ 2、 https://fastthread.io/

CPU占用率高

  1. 通过top命令找到进程和线程。
  2. 使用jstack打印线程快照。
  3. 找到线程快照正在执行的方法,并优化性能。
bash 复制代码
top -c #找到进程id
top -p 进程id #再摁H可以查看进程的线程
jstack 进程id #查看栈信息
printf '%x\n' #通过16进制,在文件中查看线程信息(文件中线程id是16进制)
# 找到栈信息中源代码位置

关注状态为RUNNABLE的线程。一些线程执行本地方法时并不会消耗CPU,只是在等待。但 JVM 仍然会将它们标识成"RUNNABLE"状态。

接口响应时间长

方法1:通过arthas的trace和watch命令,监控方法的执行耗时和参数、返回值等信息,定位性能瓶颈,并优化性能。

  1. trace 类名 方法名
  2. --skipJDKMethod false 输出JDK核心包中的方法及耗时。
  3. '#cost > 毫秒值' 。
  4. --n 数值 ,最多显示该数值条。
  5. stop结束监控,重置arthas增强的对象。

方法2:通过arthas的profile火焰图功能,找到火焰图中顶部较平的方法,一般就是性能问题产生的根源,并优化性能。

  1. 使用了for循环向ArrayList中添加数据。ArrayList扩容需要copyof复制老到新的数组。

线程不可用、死锁

死锁或时间长:通过jstack、visualvm、fastthread.io 等工具,找到线程死锁的原因,解决死锁问题。

使用线程快照生成工具就可以看到死锁的根源。文件中搜索deadlock

https://fastthread.io/

解决思路:

1、检测是否有死锁产生,无法自动解除的死锁会将线程永远阻塞。

2、如果没有死锁,再使用案例1的打印线程栈的方法检测线程正在执行哪个方法,一般这些大量出现的方法就是慢方法。

判断方法耗时

使用OpenJDK中的jmh基准测试框架对某些特定的方法比如加密算法进行基准测试,jmh可以完全模拟运行环境中的Java虚拟机参数,同时支持预热能通过JIT执行优化后的代码获得更为准确的数据。注意添加黑洞消费,否则会被优化。

官网地址:https://github.com/openjdk/jmhc

Date 和 LocalDateTime

相关推荐
书院门前细致的苹果4 小时前
JVM 全面详解:深入理解 Java 的核心运行机制
java·jvm
稻草人想看远方6 小时前
GC垃圾回收
java·开发语言·jvm
我真的是大笨蛋8 小时前
从源码和设计模式深挖AQS(AbstractQueuedSynchronizer)
java·jvm·设计模式
我真的是大笨蛋10 小时前
G1 垃圾收集器深入解析
java·jvm·笔记·缓存
好多1713 小时前
《JVM如何排查OOM》
开发语言·jvm·python
getdu15 小时前
JVM第一部分
jvm
海梨花17 小时前
字节一面 面经(补充版)
jvm·redis·后端·面试·juc
Mr_Xuhhh20 小时前
项目-sqlite类的实现
java·jvm·sqlite
佛祖让我来巡山1 天前
深入理解Java对象:从创建到内存访问的JVM底层机制
jvm·对象创建过程·对象是如何创建的
用手手打人1 天前
JVM详解(一)--JVM和Java体系结构
jvm