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

实践项目中的JVM调优原创

4年前
908445

背景

有个项目可以实时对应用的健康情况进行监控、分析、预警。如下图展示一段时间内应用“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
请先登录,查看4条精彩评论吧
快去登录吧,你将获得
  • 浏览更多精彩评论
  • 和开发者讨论交流,共同进步

为你推荐

随机一门技术分享之Netty

随机一门技术分享之Netty

从 Linux 内核角度探秘 JDK MappedByteBuffer

从 Linux 内核角度探秘 JDK MappedByteBuffer

MappedByteBuffer VS FileChannel:从内核层面对比两者的性能差异

MappedByteBuffer VS FileChannel:从内核层面对比两者的性能差异

5
4