JVM调优路线图

发布时间:2024年01月04日

JVM调优

系统调优&JVM调优

明白自己“JVM调优”在整个系统调优的位置。

优先原则:

优先架构调优和代码调优,JVM是不得已的手段,在既定的架构和代码逻辑下,JVM调整效果有限。就像“一个人本身长的不高,穿的鞋子衣服再修身增高,能高到哪里去”,架构和编码就是你的骨骼, JVM调优就是你的增高鞋垫。

调优的指标

触发点:为了解决“并发上不去”,“CPU过高”,“内存溢出”等问题开始调优。

调优不是优化,调试说明要找到一个黄金分割点,而不是解决某个问题,那是修复BUG。既然是调试,那么在哪几个指标中间调就需要明确。

指标项:

  1. 吞吐量:运行用户代码的时间占总运行时间的比例 (总运行时间=程序的运行时间+内存回收的时间)
  2. 暂停时间:执行垃圾收集时,程序的工作线程被暂停的时间
  3. 内存占用:Java堆区所占的内存大小

这三者共同构成一个“不可能三角”。三者总体的表现会随着技术进步而越来越好。一款优秀的收集器通常最多同时满足其中的两项。

相互影响:扩大堆内存,会增加“暂停时间”,但会降低GC次数。明显的暂停会对通途量有严重的影响,如果内存没法扩展,在最大吞吐量优先的情况下,降低停顿时间。

JVM调整原则

需要明白JVM有哪些开关,已经这些开关调整会产生的影响。

FullGC后的老年代空间占用

FullGC 后老年代依然存在的对象,对应代码中:static或者存储的缓存数据。

堆设置

-Xms和-Xmx 堆内存初始化和最大的值:通常设置为相同的值,避免运行时要不断扩展JVM内存

建议:扩大至3-4倍“FullGC后的老年代空间占用”

年轻代设置

参数-Xmn,1-1.5倍FullGC之后的老年代空间占用

避免新生代设置过小,当新生代设置过小时,会带来两个问题:

1. minor GC次数频繁

2.导致minor GC对象直接进老年代。当老年代内存不足时,会触发Full GC

避免新生代设置过大,当新生代设置过大时,会带来两个问题:

1.老年代变小,可能导致Full GC频繁执行

2. minor GC 执行回收的时间大幅度增加

老年代设置

注重低延迟的应用

老年代使用并发收集器,所以其大小需要小心设置,一般要考虑并发会话率和会话持续时间等一些参数

如果堆设置偏小,可能会造成内存碎片、高回收频率以及应用暂停

如果堆设置偏大,则需要较长的收集时间

方法区设置

基于jdk1.7版本,永久代:参数-XX:PermSize和-XX:MaxPermSize

基于jdk1.8版本,元空间:参数 -XX:MetaspaceSize和-XX:MaxMetaspaceSize

通常设置为相同的值,避免运行时要不断扩展

建议扩大至1.2-1.5倍“FullGc后的永久带空间占用”

GC选择

这点体会不是很深。

GC发展阶段

SerialParallel(并行) CMS(并发) G1ZGC 截至jdk1.8 ,一共有7款不同垃圾收集器。每一款不同的垃圾收集器都有不同的特点,在具体使用的时候,需要根据具体的情况选择不同的垃圾回收器

https://pics3.baidu.com/feed/faedab64034f78f06284adb11912f55db2191c94.png@f_auto?token=041c442fba413be6b5ffa9693bfe408a

G1的适用场景

面向服务端应用,针对具有大内存、多处理器的机器。(在普通大小的堆里表现并不惊喜)

最主要的应用是需要低GC延迟并具有大堆的应用程序提供解决方案(G1通过每次只清理一部分而不是全部Region的增量式清理来保证每次GC停顿时间不会过长)

在堆大小约6GB或更大时,可预测的暂停时间可以低于0.5秒

用来替换掉JDK1.5中的CMS收集器,以下情况,使用G1可能比CMS好

  1. 超过50% 的java堆被活动数据占用
  2. 对象分配频率或年代提升频率变化很大
  3. GC停顿时间过长(大于0.5至1秒)

从经验上来说,整体而言:

小内存应用上,CMS大概率会优于 G1;

大内存应用上,G1则很可能更胜一筹。 这个临界点大概是在 6~8G 之间(经验值)

其他收集器适用场景

如果你想要最小化地使用内存和并行开销,请选择Serial Old(老年代) + Serial(年轻代)

如果你想要最大化应用程序的吞吐量,请选择Parallel Old(老年代) + Parallel(年轻代)

如果你想要最小化GC的中断或停顿时间,请选择CMS(老年代) + ParNew(年轻代)

JVM调优步骤

监控分析

异常或者预警现象:

1. CPU,内存占用率超过80%,后者更高

2. MinorGC,FullGC 频繁,或者停顿过长(超过1秒)

3. 日志打印异常

4. 系统运行一段时间后卡顿或者吞吐量下降

5. 系统启动后就占用大量内存

调优目标

调优的最终目的都是为了应用程序使用最小的硬件消耗来承载更大的吞吐量或者低延迟。 jvm调优主要是针对垃圾收集器的收集性能优化,减少GC的频率和Full GC的次数,令运行在虚拟机上的应用能够使用更少的内存、高吞吐量、低延迟。

需要优化标准

下面列举一些JVM调优的量化目标参考实例

注意:不同应用的JVM调优量化目标是不一样的。

  1. 堆内存使用率<=70%;
  2. 老年代内存使用率<=70%;
  3. avgpause<=1秒;
  4. Full GC次数0或avg pause interval>=24小时 ;

目标先后

调优先后:满足程序的内存使用需求 ==》 解决时间延迟 ==》 通途量。每一个步骤都是进行下一步的基础,不可逆行之。

应用过程

1. 先找一个服务在本地调试,找到明显的问题

2. 找一个线上实例进行验证

3. 最后推广到机器,并进行后续跟踪

JVM调优工具

jps:JVM Process Status Tool

jstat:JVM Statistics Monitoring Tool

jinfo:Java Configuration Info

jmap:Memory Map

jhat:Java Heap Analysis Tool

jstack:Java Stack Trace

prof:Heap/CPU Profiling Tool

Jconsole:Java Monitoring and Management Console

JVM参数

  1. 不稳定参数 -XX:MaxGCPauseMillis=500
  2. 非标准参数 -Xmn 新生代大小
  3. 标准参数 java -version

经验分享

吞吐量优先的应用

一般吞吐量优先的应用都有一个较大的年轻代和一个较小的老年代。原因是,这样可以尽可能回收掉大部分短期对象,减少中期的对象,而老年代尽可能存放长期存活对象

附录

STW Stop-The-World

是在垃圾回收算法执行过程中,将jvm内存冻结,停顿的一种状态。

在STW状态下,所有的线程都是停止运行的 - >垃圾回收线程除外。

当STW发生时,除了GC所需要的线程,其他的线程都将停止工作,中断了的线程知道GC线程结束才会继续任务。

STW是不可避免的,垃圾回收算法的执行一定会出现STW,而我们最好的解决办法就是减少停顿的时间。

GC各种算法的优化重点就是为了减少STW,这也是JVM调优的重点。

参考:JVM 内存模型与垃圾回收

https://blog.csdn.net/weixin_45505313/article/details/99178527#25__114

参考:java.lang.OutOfMemoryError: Metaspace 的解决

https://blog.csdn.net/weixin_45505313/article/details/114089053

文章来源:https://blog.csdn.net/weixin_42754896/article/details/135343194
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。