GC优化

通过收集GC信息,结合系统需求,明确性能目标(例如:停顿时间、GC时间在整个时间所占用的百分比),确定优化方案。例如选用合适的GC回收器、重新设置内存比例、调整JVM参数等。

img

调整堆的大小

选择堆的大小其实是一种平衡。如果分配的堆过小,则程序的大部分时间可能都消耗在GC上,没有足够的时间去运行应用程序的逻辑。如果分配的堆过大,GC停顿消耗的时间取决于堆的大小,堆越大,停顿时间可能越长。设置堆内存的大小,不能超过系统内存的容量。否则操作系统会使用磁盘来充当不足的那部分内存,会导致性能的严重下降。

命令 说明
-Xms 设置老年代初始值
-Xmx 设置老年代最大值
-Xmn 设置青年代内存大小
-Xss 设置每个线程的堆栈大小。默认为1M。

设置内存比例

各分区的大小对GC的性能影响很大。如何将各分区调整到合适的大小,分析活跃数据的大小是很好的切入点。

活跃数据的大小是指,应用程序稳定运行时长期存活对象在堆中占用的空间大小,也就是Full GC后堆中老年代占用空间的大小。可以通过GC日志中Full GC之后老年代数据大小得出,比较准确的方法是在程序稳定后,多次获取GC数据,通过取平均值的方式计算活跃数据的大小。活跃数据和各分区之间的比例关系如下:

空间 倍数
总大小 3-4 倍活跃数据的大小
新生代 1-1.5 活跃数据的大小
老年代 2-3 倍活跃数据的大小
永久代 1.2-1.5 倍Full GC后的永久代空间占用

例如,根据GC日志获得老年代的活跃数据大小为300M,那么各分区大小可以设为:

总堆:1200MB = 300MB × 4 新生代:450MB = 300MB × 1.5 老年代: 750MB = 1200MB - 450MB

这部分设置仅仅是堆大小的初始值,后面的优化中,可能会调整这些值,具体情况取决于应用程序的特性和需求。

命令 说明
-XX:NewSize 设置新生代空间的初始值大小
-XX:MaxNewSize 设置新生代空间的最大大小
-XX:NewRatio 设置新生代占堆内存的比例(young = heap / (1 + NewRatio)),默认为2

永久代与元空间

命令 说明
-XX:MaxPermSize 设置永久代的最大值(jdk1.7)
-XX:MetaspaceSize 设置元空间的初始值(jdk1.8)
-XX:MaxMetaspaceSize 设置元空间的最大值(jdk1.8)
-XX:PermSize 设置永久代的初始值(jdk1.7)

控制并发

除Serial收集器之外几乎所有的垃圾收集器使用的算法都基于多线程。合理的设置线程数量,有助于提升垃圾收集的性能。一般情况下,服务器上如果运行了多个JVM实例,则所有JVM实例的垃圾收集线程等于CPU核的数量。

命令 说明
-XX:ParallelGCThreads 设置垃圾收集线程数量