当前位置: 首页 > 后端技术 > Java

一个简单的JVM调优,投进简历!

时间:2023-04-01 22:43:59 Java

给大家分享一个Github仓库,里面有大斌编译的300多本经典计算机书籍PDF,包括C语言、C++、Java、Python、前端、数据库、操作系统、计算机网络、数据结构和算法,对于机器学习、编程生活等,可以star一下。下次找书的时候可以直接在上面搜索。仓库持续更新中~Github地址:https://github.com/Tyson0314/...大家好,我是大斌。JVM调优一直是面试官喜欢问的问题。周末在网上看到一篇关于JVM调优的文章,特分享给大家。源码地址:https://zhenbianshu.github.io背景最近对负责项目进行了性能优化,包括JVM参数的调整。这是一个简单的JVM调整。JVM参数调整后,整体性能提升了5%左右,还不错。先介绍一下项目的基本情况:项目是一个高QPS压力的web服务。单机QPS一直维持在1.5K以上。由于老机器的“拖累”,配置的heapsize为8G,其中youngarea为4G。垃圾收集浏览器使用parNew+CMS。旧状态是先查看当前GC情况,主要是使用jstat查看GC概况,然后查看gc日志分析单次GC的详细状态。使用jstat-gcutilpid1000每秒打印一次gc统计信息。可以看到单次gc平均耗时在60ms左右,还可以接受,但是YGC非常频繁,基本是一秒一次,有时候一秒两次。业务响应时间的压力会变大。然后查看gc日志。打印gc日志,需要在JVM启动参数中加入如下参数:-XX:+PrintGCDateStamps:打印gc发生时的时间戳。-XX:+PrintTenuringDistribution:gc发生时打印生成信息。-XX:+PrintGCApplicationStoppedTime:打印gc暂停时间-XX:+PrintGCApplicationConcurrentTime:打印gcinterval服务运行时间-XX:+PrintGCDetails:打印gc详情,包括gcpre/memory等-Xloggc:../gclogs/gc.log.date:指定gc日志的路径。你看到的gclog是这样的:单次GC不能直接看出问题,但是可以看到gcpause前有很多次18ms左右。分析调整YGC经常直接查看gc日志不直观,可以借用一些可视化工具帮助我们分析,[gceasy](https://gceasy.io/)是一个很好的网站,我们上传后gclog,gceasy可以帮助我们生成各种维度的图表来帮助分析。查看gceasy生成的报告,发现我们服务的gc吞吐量是95%。指JVM运行业务代码占JVM总运行时间的比例。执行gc。幸运的是,这些GC绝大部分都是YGC,单次时长可控,分布均匀,这让我们的服务能够顺利运行。解决这个问题要么减少对象的创建,要么增加youngarea。前者不是短时间内可以解决的。需要找到代码中可能存在的问题点,逐步优化。至于后者,改一下配置就可以了,但是根据我们对GC最直观的印象,如果增加youngarea,那么YGC的持续时间也会快速增加。其实,这一点大可不必过分担心。我们知道YGC的耗时是由GC标记+GC复制组成的。与GC复制相比,GC标记非常快。但是,young区的大多数对象的生命周期都很短。如果youngarea翻倍,那么GC标记时间就会翻倍,但是到GC发生的时候,大部分被标记的对象已经死亡,GC复制时间肯定不会翻倍,所以我们可以放心的增加size年轻的地区。由于更换了内存不足的老机器,我将heapsize调整为12G,youngarea保留为8G。分代调整除了GC过于频繁外,GC后每一代的平均大小也需要调整。我们知道GC的提升机制。每次GC后,JVM存活世代大于MaxTenuringThreshold的对象被提升到老年代。当然JVM对于动态年龄的计算也是有规则的:按照年龄从小到大累加它所占的大小,当累加的年龄超过幸存者区域的一半时,取这个年龄和MaxTenuringThreshold中较小的一个值,作为新的晋升年龄阈值,但是看每一代的总内存大小,还不到survivorarea的一半。所以这十五代中的对象总会在两个survivor区之间来回复制,然后观察每一代的平均大小。可以看出,四代以上的物件,有一半会保留在老区。因此,可以将这些对象直接提升到老年代,以减少在两个幸存者区域之间复制对象的性能开销。所以我将MaxTenuringThreshold的值调整为4,将存活超过四代的对象直接提升到老年代。偏向锁停顿的另一个问题是gc日志中有很多18ms左右的停顿,有时连续的会超过十次。虽然每次停顿的时间都不长,但是连续多次的时间累积起来也是非常可观的。是因为1.8以后,JVM对锁进行了优化,加入了偏向锁的概念,避免了很多不必要的加锁操作。但是一旦偏向锁遇到锁竞争,取消锁需要进入安全点,导致STW。解决方法很简单,在JVM启动参数中加上-XX:-UseBiasedLocking即可。结果调整了JVM参数后,先对服务进行了压测,发现性能确实有所提升,并没有出现严重的GC问题。之后将调整后的配置放到线上机器上进行灰度,同时收集gc日志,再次进行测试。分析。由于年轻区大小翻倍,YGC频率减半,GC吞吐量提升至97.75%。平均GC时间略有增加,从大约60ms增加到66ms,非常符合预期。由于GC时CMS也会清理youngarea,所以CMS的持续时间也会受到影响,CMS的finalmark和concurrentcleaning阶段的耗时增加,这是比较正常的。此外,我还统计了对业务的影响。因GC而超时的请求大大减少。总结总之,这是一次很成功的GC调整,让我对GC有了更深的了解,但是因为没有深入老区,所以没有复习之前学过的CMS相关知识。但是性能优化不是一蹴而就的,需要时刻关注问题,及时调整。本文已收录在Github仓库,包含计算机基础、Java基础、多线程、JVM、数据库、Redis、Spring、Mybatis、SpringMVC、SpringBoot、分布式、微服务、设计模式、架构等核心知识点。欢迎star~Github地址:https://github.com/Tyson0314/...Gitee地址:https://gitee.com/tysondai/Ja...