性能文章>实践项目中的JVM调优>

实践项目中的JVM调优原创

2年前
761234

背景

有个项目可以实时对应用的健康情况进行监控、分析、预警。如下图展示一段时间内应用“JVM监控”:

image.png

从这个视图中可以看出几个关键点:

(1)标号1中展示此时间段内FullGC次数、YoungGC次数;

(2)标号2中描述标号1该时间段内FullGC耗费的时长、YoungGC耗费的时长;

(3)标号3中描述标号1该时间段内JVM各运行时数据区占用的内存;

(4)标号4中描述非JVM内存的占用量,即元空间。

很明显该应用在短时间内发生了6次FullGC,已经大大降低的应用对外提供服务的能力,那么怎么优化呢?

解决方案

即调整JVM参数:-XX:+UseConcMarkSweepGC -XX:ParallelGCThreads=4 -XX:+UseParNewGC

(1)参数1:-XX:+UseConcMarkSweepGC -并行CMS垃圾回收器,即老年代并行垃圾回收。

(2)参数2:-XX:ParallelGCThreads=4 -GC线程数,参考CPU核数

(3)参数3:-XX:+UseParNewGC -新生代并行垃圾回收

总之,通过并发进行老年代和年轻代的垃圾回收,减少由于内存不足反复GC的次数。原因在于FullGC会使得虚拟机短暂停顿,如果是串行进行垃圾回收,且回收次数过多,那么必然会导致虚拟机更长的停顿时长,应用在这段时间内的可用性大大降低,即应用响应慢了,极端点就可能造成系统奔溃。对于流量大的应用内存很快不足,那么自然GC次数也会随之增大。合理地设置JVM中的参数是能很好解决这类问题的,当然前提是代码编写也考虑了内存分配的机制,保证在代码层面上不会造成内存泄漏,否则单纯靠设置JVM参数就能解决,那肯定是不可靠的。

这里注意下UseParNewGCUseParallelGC的区别,关注JVM参数之间配合使用,避免达到不可预期的效果:

(1)UseParNewGC:并发串行收集器,它是工作在新生代的垃圾收集器,它只是将串行收集器多线程化,除了这个并没有太多创新之处,而且它们共用了相当多的代码。它与串行收集器一样,也是独占式收集器,在收集过程中,应用程序会全部暂停。但它却是许多运行在Server模式下的虚拟机中首选的新生代收集器,其中有一个与性能无关但很重要的原因是,除了Serial收集器外,目前只有它能与CMS收集器配合工作。

(2)UseParallelGC:并行收集器,同时运行在多个cpu之间,物理上的并行收集器,跟上面的收集器一样也是独占式的,但是它最大化的提高程序吞吐量,同时缩短程序停顿时间,另外它不能与CMS收集器配合工作。

JVM性能调优总结

1、一般来说,当survivor区不够大或者占用量达到50%,就会把一些对象放到老年区。通过设置合理的Eden区、survivor区及使用率,可以将年轻对象保存在年轻代,从而避免fullGC,使用-Xmn设置年轻代的大小。

2、对于占用内存比较多的大对象,一般会选择在老年代分配内存。如果在年轻代给大对象分配内存,年轻代内存不够了,就要在Eden区移动大量对象到老年代,然后这些移动的对象可能很快消亡,因此导致fullGC。通过设置参数:-XX:PetenureSizeThreshold=1000000,单位为B,标明对象大小超过1M时,在老年代(tenured)分配内存空间。

3、一般情况下,年轻对象放在Eden区,当第一次GC后,如果对象还存活,则放在survivor区。此后,没GC一次,年龄增加1,当对象的年龄达到阈值,就被放到tenured老年区。这个阈值可以通过
-XX:MaxTenringThreshold设置。如果想让对象留在年轻代,可以设置比较大的阈值。

4、设置最小堆和最大堆:-Xmx和-Xms。稳定的堆大小对垃圾回收是有利的,获得一个稳定的堆大小的方法是设置-Xms和-Xmx的值一样,即最大堆和最小堆一样。如果这样设置,系统在运行时,堆大小理论上是
恒定的,稳定的堆空间可以减少GC次数。因此,很多服务端都会将这两个参数设置为一样的数值。稳定的堆大小虽然减少GC次数,但是增加每次GC的时间,因为每次GC要把堆的大小维持在一个区间内。

5、一个不稳定的堆并非毫无用处。在系统不需要使用大内存的时候,压缩堆空间,使得GC每次应对一个较小的堆空间,从而加快单次GC次数。基于这种考虑,JVM提供两个参数,用于压缩和扩展堆空间。
(1)-XX:MinHeapFreeRatio 参数用于设置堆空间的最小空闲比率。默认是40,当堆空间的空闲内存比率小于40,JVM便会扩展堆空间。
(2)-XX:MaxHeapFreeRatio 参数用于设置堆空间的最大空间比率。默认是70,当堆空间的空闲内存比率大于70,JVM便会压缩堆空间。
(3)当-Xmx和-Xms相等时,上面两个参数无效。

6、通过增大吞吐量提高系统性能,可以通过设置并行垃圾回收收集器。
(1)-XX:+UseParallelGC: 年轻代使用并行垃圾回收收集器。这是一个关注吞吐量的收集器,可以尽可能地减少垃圾回收时间。
(2)-XX:+UseParallelOldGC: 设置老年代使用并行垃圾回收收集器。

7、尝试使用大的内存分页:使用大的内存分页增加CPU的内存寻址能力,从而提高系统性能。-XX:+LargePageSizeInBytes 这种内存页的大小。

8、使用非占用的垃圾收集器。-XX:+UseConcMarkSweepGC 老年代使用CMS收集器降低停顿。

9、-XXSurvivorRatio=3,表示年轻代中的分配比率:survivor:eden = 2:3

JVM性能调优工具

(1)jps(Java Process Status):输出JVM中运行的进程状态信息(现在一般使用jconsole)
(2)jstack:查看java进行内线程的堆栈信息。
(3)jmap:用于生成堆转存快照。
(4)jhat:用于分析jmap生成的堆转存快照(一般不推荐使用,而是使用ecplise Memory Analyzer)
(5)jstat:JVM统计监测工具。可以用来显示垃圾回收信息、类加载信息、新生代统计信息等。
(6)VisualVM:故障处理工具

点赞收藏
coffeeboy
请先登录,查看3条精彩评论吧
快去登录吧,你将获得
  • 浏览更多精彩评论
  • 和开发者讨论交流,共同进步

为你推荐

【全网首发】一次想不到的 Bootstrap 类加载器带来的 Native 内存泄露分析

【全网首发】一次想不到的 Bootstrap 类加载器带来的 Native 内存泄露分析

记一次“雪花算法”造成的生产事故的排查记录

记一次“雪花算法”造成的生产事故的排查记录

【全网首发】一次疑似 JVM Native 内存泄露的问题分析

【全网首发】一次疑似 JVM Native 内存泄露的问题分析

解读JVM级别本地缓存Caffeine青出于蓝的要诀2 —— 弄清楚Caffeine的同步、异步回源方式

解读JVM级别本地缓存Caffeine青出于蓝的要诀2 —— 弄清楚Caffeine的同步、异步回源方式

单服务并发出票实践

单服务并发出票实践

刺激,线程池的一个BUG直接把CPU干到100%了。

刺激,线程池的一个BUG直接把CPU干到100%了。

4
3