JVM GC调优一则--增大Eden Space提高性能

缘起

线上有Tomcat升级到7.0.52版,然后有应用的JVM FullGC变频繁,在高峰期socket连接数,Cpu使用率都暴增。

思路

思路是Tomcat本身的代码应该是没有问题的,有问题的可能是应用代码升级,或者环境改变了,总之Tomcat的优先级排在最后。

先把应用的heap dump下来分析下:

jmap -dump:format=b,file=path pid

用IBM的Heap Analyser分析,发现dubbo rpc调用的RpcInvocation对象和taglibs的SimpleForEachIterator对象占用了很大部分内存。

正常来说,这两种类型的对象都应该可以很快被回收掉,怎么会占用了那么大的内存空间?是不是有别的对象引用了它们,导致不能释放?

再仔细分析,发现RpcInvocation对象都是root refer的,也就是根对象,正常来说根对象应该可以很快就被回收掉的,为什么在内存中会有那么多对象?

再查看应用的JVM参数:

-Xms2g -Xmx2g -Xmn256m -XX:SurvivorRatio=8 -XX:ParallelGCThreads=8 -XX:PermSize=512m -XX:MaxPermSize=512m -Xss256k -XX:-DisableExplicitGC -XX:+UseCompressedOops -XX:+UseConcMarkSweepGC -XX:+CMSParallelRemarkEnabled

首先发现应用的新生代,即-Xmn256m 设置得太小了。对照上面RpcInvocation对象占用了226M,SimpleForEachIterator占用了267M内存。

显然在新生代里,没办法放下那么多的对象,这些对象必然是被放到老生代(old space)里去了。

既然RpcInvocation对象和SimpleForEachIterator对象应该都是可以很快被回收了,那么思路变成,触发一下线上的FullGC,看下对象有没有被回收。

在触发之前,先用jmap -histo pid统计下对象的数量:

34:        136762        4376384  com.alibaba.dubbo.rpc.RpcInvocation

129:         16345         392280  org.apache.taglibs.standard.tag.common.core.ForEachSupport$SimpleForEachIterator

用 jmap -histo:live <pid> 触发Full GC之后:

294:           625          20000  com.alibaba.dubbo.rpc.RpcInvocation

495:           292           7008  org.apache.taglibs.standard.tag.common.core.ForEachSupport$SimpleForEachIterator

果然数量大大的减少了。

所以结论比较明显了,新生代(Young generation)的空间太小,导致有一些本应该可以很快就被回收的对象被放到了老生代(Old generation)里,导致老生代上涨很快,频繁Full GC。

于是想办法增加新生代的大小,把JVM参数改为:

 -Xms2g -Xmx2g -XX:ParallelGCThreads=8 -XX:PermSize=256m -XX:MaxPermSize=512m -Xss256k -XX:-DisableExplicitGC -XX:+UseCompressedOops -XX:+UseConcMarkSweepGC -XX:+CMSParallelRemarkEnabled 

因为观察到PermSize实际上只用了不到200M,没有必要设置为512M,浪费内存,所以改为 -XX:PermSize=256m -XX:MaxPermSize=512m 。

另外,把新生代最大限制-Xmn256m 去掉。因为默认的NewRatio = 2,即除了PermSize,新生代大约占内存的1/3,即约(2048 - 256) /3 = 597M。和原来相比增大了一倍不止。

修改上线之后,观察发现Old Space增长缓慢,FullGC次数大大减少,时间在50ms下,Yong GC都在10ms下,达到了想要的效果。

简单的GC过程分析

首先来看一张GC的模型图,很形象:

简单来说,对于GC,我们了解到这些信息就足够了。

大部分新对象在Eden Space上分配,当Eden Space满了,则要用到Survivor Space来回收。YGC的算法是很快的。

多次YGC之后,还存活的对象就会被移到Old Generation(old space)上,当Old Generation满了的时候,就会FGC,FGC有通常比较慢。

Permanent Space只要你在开始时分配了足够大的空间,那它可以不用管。

我们可以得出一些结论:

  • 合理减少对象进入老生代;
  • Old Space可能会一直增长,有时没有办法避免不让对象进入Old Space,当然也有一些程序是从来都不执行FGC的;
  • 是不是尽全力防止对象进入老生代?显然不是,有些对象如果长久存在在新生代里,显然加重了YGC的负担,多次YGC之后仍然存活的对象显然应该放到Old Space里。

理想的GC/内存使用情况

总结下来,可以发现,理想的GC情况应该是这样的:

Old Space增长缓慢,FullGC次数少,FullGC的时间短(大部情况应该要在1秒内)。

总结:

尽量少加上一些默认参数。这点我很赞同RednaxelaFX的看法,配置了默认参数除了让后面调优的人蛋疼之外,没有太多的帮助。

GC调优就是一个取舍权衡的过程,有得必有失,是好可以在多个不同的实例里,配置不同的参数,然后进行比较。

有很多命令行工具或者图形工具可以使用,好的工具事半功倍。

参考:

http://www.alphaworks.ibm.com/tech/heapanalyzer?    IBM Heap Analyser

http://hllvm.group.iteye.com/group/topic/27945    JVM调优的"标准参数"的各种陷阱,RednaxelaFX 出品,强列推荐

http://www.taobaotesting.com/blogs/2392      JAVA性能剖析1——JVM内存管理与垃圾回收

http://www.oschina.net/translate/using-headless-mode-in-java-se      在 Java SE 平台上使用 Headless 模式

JVM GC调优一则--增大Eden Space提高性能,布布扣,bubuko.com

时间: 2024-08-07 04:31:44

JVM GC调优一则--增大Eden Space提高性能的相关文章

深入JVM系列(二)之GC机制、收集器与GC调优(转)

一.回顾JVM内存分配 需要了解更多内存模式与内存分配的,请看 深入JVM系列(一)之内存模型与内存分配 1.1.内存分配: 1.对象优先在EDEN分配2.大对象直接进入老年代 3.长期存活的对象将进入老年代 4.适龄对象也可能进入老年代:动态对象年龄判断 动态对象年龄判断: 虚拟机并不总是要求对象的年龄必须达到MaxTenuringThreshold才能晋升到老年代,当Survivor空间的相同年龄的所有对象大小总和大于Survivor空间的一半,年龄大于或等于该年龄的对象就可以直接进入老年代

深入JVM系列(二)之GC机制、收集器与GC调优

一.回想JVM内存分配 须要了解很多其它内存模式与内存分配的,请看 深入JVM系列(一)之内存模型与内存分配 1.1.内存分配: 1.对象优先在EDEN分配 2.大对象直接进入老年代 3.长期存活的对象将进入老年代 4.适龄对象也可能进入老年代:动态对象年龄推断 动态对象年龄推断: 虚拟机并不总是要求对象的年龄必须达到MaxTenuringThreshold才干晋升到老年代,当Survivor空间的同样年龄的全部对象大小总和大于Survivor空间的一半,年龄大于或等于该年龄的对象就能够直接进入

JVM的GC机制及JVM的调优方法

内存管理和垃圾回收是JVM非常关键的点,对Java性能的剖析而言,了解内存管理和垃圾回收的基本策略非常重要. 1.在程序运行过程当中,会创建大量的对象,这些对象,大部分是短周期的对象,小部分是长周期的对象,对于短周期的对象,需要频繁地进行垃圾回收以保证无用对 象尽早被释放掉,对于长周期对象,则不需要频率垃圾回收以确保无谓地垃圾扫描检测.为解决这种矛盾,Sun JVM的内存管理采用分代的策略. 1)年轻代(Young Gen):年轻代主要存放新创建的对象,内存大小相对会比较小,垃圾回收会比较频繁.

JVM层GC调优(上)

JVM内存结构简介(jdk1.8) JVM层的GC调优是生产环境上必不可少的一个环节,因为我们需要确定这个进程可以占用多少内存,以及设定一些参数的阀值.以此来优化项目的性能和提高可用性,而且这也是在面试中经常会被问到的问题. 想要进行GC调优,我们首先需要简单了解下JVM的内存结构,Java虚拟机的规范文档如下: https://docs.oracle.com/javase/specs/jvms/se8/html/index.html 在介绍JVM内存结构之前,我们需要先知道运行时数据区这样的一

JVM层GC调优(下)

GC日志格式 本文是 JVM层GC调优(上) 的后续,在上一篇文章中,我们介绍了JVM的内存结构.常见的垃圾回收算法以及垃圾收集器和不同收集器中的一些GC调优参数.所以通过上文,我们也对GC相关的内容有了一定的了解. 但是光知道一些调优参数是没用的,我们需要能够从GC的日志中去分析可以调优的地方,这样才能使用这些参数去进行相应的调整,所以本小节将介绍一下不同收集器的GC日志格式. 我们这里以Tomcat为例,首先需要在Tomcat的catalina.sh脚本文件中加入打印GC日志的相关参数,如下

jvm性能监控与GC调优

目录 一 提出问题 二 基于JDK命令行工具的监控 1. JVM的三种参数类型 1.1 标准参数 1.2 X 参数 1.3 XX 参数 1.4 常用命令 2. jstat查看虚拟机统计信息 2.1 类加载信息 2.2 垃圾回收信息 2.3 JIT编译信息 3. jmap + MAT分析内存溢出 [实战] 3.1 模拟内存溢出 3.2 导出内存影像文件 3.3 使用MAT分析dump文件 4. jstack分析死循环与死锁 [实战] 三 基于JVisualVM的可视化监控 四 基于Btrace的监

GC 调优(实战篇) - GC参考手册

本章介绍导致GC性能问题的典型情况.相关示例都来源于生产环境, 为演示需要做了一定长度的精简. 说明: Allocation Rate, 翻译为分配速率, 而不是分配率; 因为不是百分比,而是单位时间内分配的量; 同理, Promotion Rate 翻译为 提升速率; 您应该已经阅读了前面的章节: 垃圾收集简介 - GC参考手册 Java中的垃圾收集 - GC参考手册 GC 算法(基础篇) - GC参考手册 GC 算法(实现篇) - GC参考手册 GC 调优(基础篇) - GC参考手册 GC

JVM参数调优与垃圾回收机制

自动内存管理机制 Java虚拟机原理  所谓虚拟机,就是一台虚拟的机器.他是一款软件,用来执行一系列虚拟计算指令,大体上虚拟机可以分为 系统虚拟机和程序虚拟机, 大名鼎鼎的Visual Box.Vmare就属于系统虚拟机,他们完全是对物理计算的仿真, 提供了一个可以运行完整操作系统的软件平台. 程序虚拟机典型代码就是Java虚拟机,它专门为执行单个计算程序而计算,在Java虚拟机中执行的指令我们成为Java 自己码指令.无论是系统虚拟机还是程序虚拟机,在上面运行的软件都被限制于虚拟机提供的资源中

【学习】011 JVM参数调优配置

自动内存管理机制 Java虚拟机原理 所谓虚拟机,就是一台虚拟的机器.他是一款软件,用来执行一系列虚拟计算指令,大体上虚拟机可以分为 系统虚拟机和程序虚拟机, 大名鼎鼎的Visual Box.Vmare就属于系统虚拟机,他们完全是对物理计算的仿真, 提供了一个可以运行完整操作系统的软件平台. 程序虚拟机典型代码就是Java虚拟机,它专门为执行单个计算程序而计算,在Java虚拟机中执行的指令我们成为Java 自己码指令.无论是系统虚拟机还是程序虚拟机,在上面运行的软件都被限制于虚拟机提供的资源中.