前言
在前面两篇文章已经介绍了有关GC的算法以及垃圾回收器的选择,今天这篇来讲下如何针对GC进行JVM调优。
正文
堆大小的调优
一般来说,堆越大越好
- 降低GC频率,但过大可能会增加单次GC的时间
- 对象更有可能成为垃圾
受硬件和操作系统限制
- 32位操作系统单个进程的最大可用内存为2G,64位无限制
- 小心平衡New和Old的比例
参数
- -Xms=1024MB:堆的最小值
- -Xmx=1024MB:堆的最大值
- 堆每次调整都会触发一次Full GC (避免频繁调整,可以设置 -Xms=-Xmx)
-Xms==-Xmx?
- 设置-Xms为堆的预期大小
- 堆调整的代价很大
- 如果内存允许,设置-Xmx为一个比-Xms更大的数值
- 以防万一
- 也许系统负载比你想的要重
- 随着时间的推移,数据量越来越大
- ……
- 进行一次Full GC & 堆调整总比发生OOM & 宕机要好
新生代调优-大小
- 增加Eden的大小会
- 降低minor GC的频率
- 但不一定会增大minor GC的时间
- minor GC的耗时和要拷贝的对象数量,即存活对象多少成正比
- 参数
- -XX:NewSize=1024MB : 新生代初始大小
- -XX:MaxNewSize=1024MB : 新生代最大值
- -XX:NewRatio=m : New 和 Old 的比值
- -Xmn=1024MB : 新生代大小
- 出于性能考虑,一般使用-Xmn来固定新生代大小
新生代调优-晋升
- 尽可能地让对象待在survivor中,使之在新生代被回收
- 减少晋升到老年代的对象
- 降低老年代 GC 的频率
- 但是同时,避免长时间存活的对象在survivor间不必要的拷贝
- 增加minor GC不必要的开销
- 不容易找到平衡点
- 原则上:better copy more,than promote more
- -XX:SurvivorRatio=m : Eden 和 Survivor 的比值
- 对象晋升年龄的阀值:Tenuring Threshold
- 年龄标志位age, 每熬过一轮GC对象年龄加1
- Serial Copying 和 ParNew 每次minor GC后重新计算Tenuring Threshold的规则:
- 参数-XX:TargetSurvivorRatio=n : minor GC 后Survivor预期被占用的比例
- 计算Desired Survivor Size = Survivor大小 * TargetSurvivorRatio
- 统计存活对象的年龄,若在某个年龄上的对象总大小 > Desired Survivor Size, 则 TenuringThreshold = min(该年龄,MaxTenuringThreshold)
- 否则 TenuringThreshold = MaxTenuringThreshold
- 下次 minor GC的阀值就以此为准
- 查看每次 minor GC 后年龄的分部和计算出来的 TenuringThreshold:
- -XX:+PrintTenuringDistribution
老年代调优
- 尽可能地调优新生代
- 尤其要注意 CMS 中的 Promotion
- free list
- 更容易出现内存碎片
- 尤其要注意 CMS 中的 Promotion
- (对CMS)在不紧要的时间段手动进行Full GC
- 清理堆,压缩,减少内存碎片
- 大小的平衡
- 太大 —— 单次GC时间长;
- 太小 —— GC频率高
- 硬件优化
- 加CPU
- 程序优化,避免无用对象浪费Old空间
- 去掉不必要的缓存
- oracle 10g驱动时preparedstatement cache太大