概述
在第一篇文章简单理解 Java 垃圾收集器,我们了解了不同 GC 算法的流程,GC 是如何工作的,什么是年轻代和老年代,你应该了解的 JDK 7 中的 5 种类型的 GC。
在第二篇文章如何监控 Java 垃圾回收,已经解释了如何监控 GC,以及我们可以使用哪些工具来使这个过程更快更有效。
在这篇文章中将展示一些可用于 GC 调优的最佳选项,阅读这篇文章的前提是你已经理解了本系列的前几篇文章。因此,为了你的进一步理解,如果你还没有阅读前两篇文章,请在阅读本文之前先阅读。
是否需要 GC 调优?
或者更准确地说 基于 Java 的服务 是否需要 GC 调优?事实上并不是所有基于 Java 的服务都需要GC 调优,也就是说运行中的基于 Java 的系统具有以下选项和操作通常不需要进行 GC 调优:
- 已使用
-Xms
和–Xmx
选项指定内存大小。 - 系统中没有超时日志。
换句话说,如果你没有设置内存大小,并且打印了太多的Timeout日志,你就需要对你的系统进行GC调优了。
但是,要记住一件事:GC 调优是最后一个要完成的任务。
想想GC调优的根本原因:垃圾收集器清除在 Java 中创建的对象,需要清除的对象数量以及要执行的 GC 次数取决于已创建的对象数量。因此要控制系统执行的 GC,首先应该减少创建的对象数量。
- 我们可以用
StringBuilder
或StringBuffer
来代替String
,减少创建字符串对象。 - 并且最好尽可能少地积累日志。
我们知道在某些情况下我们无能为力,我们已经看到 XML 和 JSON 解析过程中会使用比较多的内存,尽管我们减少 String
的使用,并尽可能地处理日志,但仍会使用巨大的临时内存来解析 XML 或 JSON,大约 10-100 MB。我们很难不使用 XML 和 JSON,只要明白它需要太多的内存。
如果应用程序内存使用率在反复调优后有所改善,则可以开始 GC 调优,我们将 GC 调优的目的分为两种:
-
尽量减少传递到老年代的对象数量。
-
减少 Full GC 的执行时间。
尽量减少传递到老年代的对象数量
分代 GC 是 Oracle JVM 提供的 GC,不包括 JDK 7 及更高版本可以使用的 G1 GC。换句话说,在 Eden 区域中创建一个对象并从 Survivor0 区域转移和转移到 Survivor1 区域,达到一定的次数之后,剩下的对象被移动到老年代。有些对象是在Eden区创建的,因为体积大,直接传递到Old区。Old 区的 GC 比 New 区的 GC 花费的时间相对更多。因此减少传递到 Old 区域的对象数量可以降低 full GC 的频率,减少传递到 Old 区的对象数量可能会被误解为选择将对象留在 New 区,然而并不是这样,相反,你可以调整新生代区域的大小。
减少 Full GC 时间
Full GC 的执行时间比 Minor GC 的执行时间相对较长,因此如果Full GC 执行时间过长(1 秒或以上),可能会导致多个连接部分发生超时。
- 如果尝试减小 Old 区域大小以减少 Full GC 执行时间,则可能会发生
OutOfMemoryError
或 Full GC 的次数可能会增加。 - 如果尝试增加 Old 区域大小来减少 Full GC 的次数,则会增加执行时间。
因此,我们需要将 Old 区域大小设置为“适当”的值。
影响 GC 性能的选项
前面我们提到,有人在设置某个GC选项时性能很好,为什么我们不像他那样使用那个选项? 原因是不同 web 服务中的对象大小和生命周期是不同的。
简单想一下,如果在A、B、C、D、E的条件下执行一项任务,而在只有 A 和 B 的条件下执行相同的任务,那么哪个任务完成得更快?从常识的角度来看,答案将是在 A 和 B 条件下执行的任务。Java GC 选项是相同的,设置多个选项并不会提高执行 GC 的速度,相反它可能会使 GC 变慢。
GC 调优的基本原理是将不同的 GC 选项应用到两个或多个服务器上并进行比较,然后通过性能分析选择表现出增强的性能或更好的 GC 时间的服务器设置的选项。
下表显示了可能影响性能的 GC 选项中与内存大小相关的选项。
表 1:要为 GC 调整检查的 JVM 选项。
分类 | 选项 | 描述 |
---|---|---|
堆区大小 | -Xms |
启动 JVM 时的堆区大小 |
-Xmx |
最大堆区大小 | |
新生代区域大小 | -XX:NewRatio |
新生代与老年代比例 |
-XX:NewSize |
新生代大小 | |
-XX:SurvivorRatio |
Eden 区与 Survivor |