首页 > 其他分享 >一次简单的 JVM 调优

一次简单的 JVM 调优

时间:2024-09-06 09:38:24浏览次数:6  
标签:log young 调优 gc JVM 简单 GC 时长

背景


最近对负责的项目进行了一次性能优化,其中包括对 JVM 参数的调整,算是进行了一次简单的 JVM 调优,JVM 参数调整之后,服务的整体性能有 5% 左右的提升,还算不错。

先介绍一下项目的基本情况:

项目是一个高 QPS 压力的 web 服务,单机 QPS 一直维持在 1.5K 以上,由于旧机器的”拖累”,配置的堆大小是 8G,其中 young 区是 4G,垃圾回收器用的是 parNew + CMS

旧状


首先是查看当前 GC 的情况,主要是使用 jstat 查看 GC 的概况,再查看 gc log,分析单次 gc 的详细状况。

使用 jstat -gcutil pid 1000 每隔一秒打印一次 gc 统计信息。

 

 

可以看到,单次 gc 平均耗时是 60ms 左右,还算可以接受,但 YGC 非常频繁,基本上每秒一次,有的时候还会一秒两次,在一秒两次的时候,服务对业务响应时长的压力就会变得很大。

接着查看 gc log,打印 gc log 需要在 JVM 启动参数里添加以下参数:

  • -XX:+PrintGCDateStamps:打印 gc 发生的时间戳。
  • -XX:+PrintTenuringDistribution:打印 gc 发生时的分代信息。
  • -XX:+PrintGCApplicationStoppedTime:打印 gc 停顿时长
  • -XX:+PrintGCApplicationConcurrentTime:打印 gc 间隔的服务运行时长
  • -XX:+PrintGCDetails:打印 gc 详情,包括 gc 前/内存等。
  • -Xloggc:../gclogs/gc.log.date:指定 gc log 的路径

看到的 gc log 形如:

 

 

单次 GC 方面并不能直接看出问题,但可以看到 gc 前有很多次 18ms 左右的停顿。

分析和调整


YGC 频繁

直接查看 gc log 并不直观,我们可以借用一些可视化工具来帮助我们分析, [gceasy](https://gceasy.io/) 是个挺不错的网站,我们把 gc log 上传上去后, gceasy 可以帮助我们生成各个维度的图表帮助分析。

查看 gceasy 生成的报告,发现我们服务的 gc 吞吐量是 95%,它指的是 JVM 运行业务代码的时长占 JVM 总运行时长的比例,这个比例确实有些低了,运行 100 分钟就有 5 分钟在执行 gc。幸好这些 GC 中绝大多数都是 YGC,单次时长可控且分布平均,这使得我们服务还能平稳运行。

解决这个问题要么是减少对象的创建,要么就增大 young 区。前者不是一时半会儿都解决的,需要查找代码里可能有问题的点,分步优化。

而后者虽然改一下配置就行,但以我们对 GC 最直观的印象来说,增大 young 区,YGC 的时长也会迅速增大。

其实这点不必太过担心,我们知道 YGC 的耗时是由 GC 标记 + GC 复制 组成的,相对于 GC 复制,GC 标记是非常快的。而 young 区内大多数对象的生命周期都非常短,如果将 young 区增大一倍,GC 标记的时长会提升一倍,但到 GC 发生时被标记的对象大部分已经死亡, GC 复制的时长肯定不会提升一倍,所以我们可以放心增大 young 区大小。

由于低内存旧机器都被换掉了,我把堆大小调整到了 12G,young 区保留为 8G。

分代调整

除了 GC 太频繁之外,GC 后各分代的平均大小也需要调整。

 

 

我们知道 GC 的提升机制,每次 GC 后,JVM 存活代数大于 MaxTenuringThreshold 的对象提升到老年代。

当然,JVM 还有动态年龄计算的规则:按照年龄从小到大对其所占用的大小进行累积,当累积的某个年龄大小超过了 survivor 区的一半时,取这个年龄和 MaxTenuringThreshold 中更小的一个值,作为新的晋升年龄阈值,但看各代总的内存大小,是达不到 survivor 区的一半的。

 

 

所以这十五个分代内的对象会一直在两个 survivor 区之间来回复制,再观察各分代的平均大小,可以看到,四代以上的对象已经有一半都会保留到老年区了,所以可以将这些对象直接提升到老年代,以减少对象在两个 survivor 区之间复制的性能开销。

所以我把 MaxTenuringThreshold 的值调整为 4,将存活超过四代的对象直接提升到老年代。

偏向锁停顿

还有一个问题是 gc log 里有很多 18ms 左右的停顿,有时候连续有十多条,虽然每次停顿时长不长,但连续多次累积的时间也非常可观。1.8 之后 JVM 对锁进行了优化,添加了偏向锁的概念,避免了很多不必要的加锁操作,但偏向锁一旦遇到锁竞争,取消锁需要进入 safe point,导致 STW

解决方式很简单,JVM 启动参数里添加 -XX:-UseBiasedLocking 即可。

结果


调整完 JVM 参数后先是对服务进行压测,发现性能确实有提升,也没有发生严重的 GC 问题,之后再把调整好的配置放到线上机器进行灰度,同时收集 gc log,再次进行分析。

由于 young 区大小翻倍了,所以 YGC 的频率减半了,GC 的吞量提升到了 97.75%。平均 GC 时长略有上升,从 60ms 左右提升到了 66ms,还是挺符合预期的。

由于 CMS 在进行 GC 时也会清理 young 区,CMS 的时长也受到了影响,CMS 的最终标记和并发清理阶段耗时增加了,也比较正常。另外我还统计了对业务的影响,之前因为 GC 导致超时的请求大大减少了。

小结


总之,这是一次挺成功的 GC 调整,让我对 GC 有了更深的理解,但由于没有深入到 old 区,之前学习到的 CMS 相关的知识还没有复习到。

不过性能优化并不是一朝一夕的事,需要时刻关注问题,及时做出调整。

标签:log,young,调优,gc,JVM,简单,GC,时长
From: https://www.cnblogs.com/ataoxz/p/18259865

相关文章

  • Go简单实现几种常用的限流
    固定窗口packagemainimport("fmt""sync""sync/atomic""time")//定义限流结构体typeRateLimiterstruct{intervaltime.Duration//时间窗口tokensint32//令牌总数lastTimeint64......
  • 推荐一款流量录制回放工具:jvm-sandbox-repeater
    在软件开发和测试过程中,我们经常会遇到需要对网络请求进行录制和回放的需求,以便进行调试、测试和分析。为了模拟真实的用户请求,我们通常会使用各种流量录制回放工具来记录并重放网络请求。其中,jvm-sandbox-repeater是一款功能强大的流量录制回放工具,可以帮助我们轻松实现对网络......
  • 一次Java性能调优实践【代码+JVM 性能提升70%】
    这是我第一次对系统进行调优,涉及代码和JVM层面的调优。如果你能看到最后的话,或许会对你日常的开发有帮助,可以避免像我一样,犯一些低级别的错误。本次调优的代码是埋点系统中的报表分析功能,小公司,开发结束后,没有CodeReview环节,所以下面某些问题,也许在CodeReview环节就可以避免。......
  • 终于使用c++、结构体,函数实现简单数组元素的插入
    includeusingnamespacestd;//定义结构体structMyArray{intarr[100];//数组,假设最大长度为100intn;//数组当前元素数量};//输入函数voidscanf(MyArray&myArray,int&x,int&y){cin>>myArray.n;for(inti=0;i<myArray.n;i++){cin>>my......
  • 企业级项目实战以及多年项目开发经验大牛打造 - - JVM
    JVMjvm分区Heap(堆区):主要存储new出来的对象实例,Java堆中细分为:新生代和老年代,一个新生代分为1个Eden区和2个Survivor区,说明:绝大部分对象在Eden区生成,当Eden区装填满的时候,会触发YoungGarbageCollection,即YGC。垃圾回收的时候,在Eden区实现清除策略,没有被引用的对象则直接......
  • 用 React 构建一个简单的计算器:前端开发实战演练
    在前端开发中,React是一个非常流行的JaScript框架,广泛用于构建用户界面。今天我们将通过一个实战项目,展示如何用React构建一个简单的计算器,这不仅可以帮助你巩固对React的基础知识,还能让你对实际开发有更深的理解。一、项目搭建你需要确保本地环境已经安装了Node.js和n......
  • 一个简单API,一键实现多通道消息推送
    Spug推送助手是一个强大的多通道消息推送平台,支持电话、短信、邮件、飞书、钉钉、微信、企业微信等多种推送方式。只需一个简单的API调用,即可实现消息的快速分发。如何使用?使用推送助手非常简单,一个curl命令即可搞定:curlhttps://push.spug.cc/send/LQjEKrl?code=688522......
  • 简单的数据在内存中的存储
    目录一.整数在内存中的存储1.1原码,反码和补码(1)原码(2)反码(3)补码1.2存储方式二.大小端字节序和字节序判断2.1大小端字节序的概念2.2字节序判断 三.练习 练习1练习2练习3 练习4 练习5 练习6 后记 一.整数在内存中的存储1.1原码,反码和补码(1)原码原......
  • prompt提示词调优工具介绍-promptfoo
    认识promptfoopromptfoo是一款开源的prompt调优工具,该工具支持如下功能:支持常见OpenAI、Anthropic、Azure、Google、HuggingFace、开源模型(如Llama),或自定义API程序以用于任何LLM模型提示词调优。支持批量跑提示词;支持提示词文件导入,模型应答结果导出;还有更多功能,......
  • 2024.08.03科大讯飞飞凡计划(简单)
    1.交换树节点给定一棵树,每个节点有一个权值。现在每次可以交换任意两个节点的权值,请问最少多少次交换可以使得每个节点的权值等于它的编号?保证给出的权值是一个排列,也就是说保证一定有解。不用考虑树的关系,因为不是相邻交换```intmain(){intn;cin>>n;v......