为什么需要优化GC
或者说的更确切一些,对于基于Java的服务,是否有必要优化GC?应该说,对于所有的基于Java的服务,并不总是需要进行GC优化,但前提是所运行的基于Java的系统,包含了如下参数或行为:
- 已经通过 -Xms 和–Xmx 设置了内存大小
- 包含了 -server 参数
- 系统中没有超时日志等错误日志
换句话说,如果你没有设定内存的大小,并且系统充斥着大量的超时日志时,你就需要在你的系统中进行GC优化了。
但是,你需要时刻铭记一条:GC优化永远是最后一项任务。
想一下进行GC优化的最根本原因,垃圾收集器清除在Java程序中创建的对象,GC执行的次数即需要被垃圾收集器清理的对象个数,与创建对象的数量成正比,因此,首先你应该减少创建对象的数量。
俗话说的好,“冰冻三尺非一日之寒”。我们应该从小事做起,否则日积月累就会很难管理。
- 我们需要使用StringBuilder 或者StringBuffer 来替代String
- 应该尽量少的输出日志
但是,我们知道有些情况会让我们束手无策,我们眼睁睁的看着XML以及JSON解析占用了大量的内存。即便我们已经尽可能少的使用String以及尽量少的输出日志,大量的临时内存被用于XML或者JSON解析,例如10-100MB。但是,舍弃XML和JSON是很难的。我们只要知道,他会占用很多内存。
如果应用内存使用量重复几次调整之后增加了,你就可以开始GC优化了。
我为GC优化归纳了两个目的:
- 一个是将转移到老年代的对象数量降到最少
- 另一个是减少Full GC的执行时间
将转移到老年代的对象数量降到最少
按代的GC机制由Oracle JVM提供,不包括可以在JDK7以及更高版本中使用的G1 GC。换句话说,对象被创建在伊甸园空间,而后转化到幸存者空间,最终剩余的对象被送到老年代。某些比较大的对象会在被创建在伊甸园空间后,直接转移到老年代空间。老年代空间上的GC处理会新生代花费更多的时间。因此,减少被移到老年代对象的数据可以显著地减少Full GC的频率。减少被移到老年代空间的对象的数量,可能被误解为将对象留在新生代。但是,这是不可能的。取而代之,你可以调整新生代空间的大小。
减少Full GC执行时间
Full GC的执行时间比Minor GC要长很多。因此,如果Full GC花费了太多的时间(超过1秒),一些连接的部分可能会发生超时错误。
- 如果你试图通过消减老年代空间来减少Full GC的执行时间,可能会导致OutOfMemoryError 或者 Full GC执行的次数会增加。
- 与之相反,如果你试图通过增加老年代空间来减少Full GC执行次数,执行时间会增加。
因此,你需要将老年代空间设定为一个“合适”的值。
影响GC性能的参数
正如我们在第二篇文章结尾提到的,不要幻想“某个人设定了GC参数后性能得到极大的提高,我们为什么不和他用一样的参数?”,因为不同的Web服务所创建对象的大小和他们的生命周期都不尽相同。
简单来说,如果一个任务的执行条件是A,B,C,D和E,同样的任务执行条件换为A和B,你会觉得哪个更快?从一般人的直觉来看,在A和B条件下执行的任务会更快。
Java GC参数也是相同的道理,设定一些参数不但没有提高GC执行速度,反而可能导致他更慢。GC优化的最基本原则是将不同的GC参数用于2台或者多台服务器,并进行对比,并将那些被证明提高了性能或者减少了GC执行时间的参数应用于服务器。请谨记这一点。
下面这个表格列出了GC参数中与内存大小相关的,可以影响性能的参数。
表1:GC优化需要考虑的Java参数
定义 | 参数 | 描述 |
堆内存空间 | -Xms | Heap area size when starting JVM 启动JVM时的堆内存空间。 |
-Xmx | Maximum heap area size 堆内存最大限制 | |
新生代空间 | -XX:NewRatio | Ratio of New area and Old area 新生代和老年代的占比 |
-XX:NewSize | New area size 新生代空间 | |
-XX:SurvivorRatio | Ratio ofEdenarea and Survivor area 伊甸园空间和幸存者空间的占比 |
我在进行GC优化时经常使用-Xms,-Xmx和-XX:NewRatio。-Xms和-Xmx是必须的。你如何设定NewRatio 会对GC性能产生十分显著的影响。有些人可能会问如何设定Perm区域的大小?你可以通过-XX:PermSize 和-XX:MaxPermSize参数来设定,
当OutOfMemoryError 错误发生并且是由于Perm空间不足导致时,另一个可能影响GC性能的参数是GC类型。下表列出了所有可选的GC类型(基于JDK6.0)
表2:GC类型可选参数
分类 | 参数 | 备考 |
Serial GC | -XX:+UseSerialGC | |
Parallel GC | -XX:+UseParallelGC | |
Parallel Compacting GC | -XX:+UseParallelOldGC | |
CMS GC | -XX:+UseConcMarkSweepGC | |
G1 | -XX:+UnlockExperimentalVMOptions | 在JDK6中这两个参数必须同时使用 |
除了G1 GC,可以通过每种类型第一行的参数来切换GC类型。最常用的GC类型是Serial GC。他专门针对客户端系统进行了优化。
影响GC性能的参数有很多,但是上面提到的参数会带来最显著的效果。请牢记,设定过多的参数不一定会减少GC执行时间。