1. 前后两次GC的间隔是否太短?
运行 java -verbose:gc -Xloggc:gc.log BigObject,文件内容如下:
69.713: [GC 11536K->11044K(12016K), 0.0032621 secs]
69.717: [Full GC 11044K->5143K(12016K), 0.1429698 secs]
首列是JVM 开始后的秒数和毫秒数的时间戳,方括号中含义的依次为:收集的类型 GC之前活动对象所占大小 GC之后活动对象所占大小 Heap总大小(不包括永久代) 本次GC持续的时间。
可以看出第一次GC为一个minor收集, 在GC开始之前,JVM运行了69.713 秒,使用了11536 Kb 的Heap空间。在完成时,使用了11044 Kb,Heap总空间为12016 Kb,而整个收集用了0.0032621 秒。
第二次GC为一个完全收集,在 69.717 秒时发生,即在上一次GC之后0.004 秒开始。注意,如果将上一次minor GC的持续时间加到其开始时间上,就会看到在上次minor收集是在本次完全收集之前不到1毫秒结束。可以得出结论:minor收集没有筹集到足够的空间,并因此触发了完全的 GC。对应用程序来说,就好像一直持续了0.1462319 秒的GC收集。
优化提示:提高年轻代的大小。建议:年轻代的大小为整个Heap空间的3/8。
2. 年轻代的回收率是否低于70%?
运行java -Xmn4m -Xms32m -Xmx32m -XX:+PrintGCDetails BigObject,输出结果如下:
[GC [DefNew: 3968K->64K(4032K), 0.0923407 secs] 3968K->2025K(32704K), 0.0931870 secs]
可以看出,Minor收集在年轻代中中找回3904K(3968K->64K),整个Heap空间找回1943K (3968K->2025)。Minor收集了大约50%(1943/3904)的对象,而另外的50%的对象则被移到了年老代。
优化提示:适当提高年轻代的大小,观察年轻代回收率是否提高。年轻代的minor收集率应在70%以上。
3. 应用暂停时间是否过长?
运行 java -XX:+PrintGCDetails -XX:+PrintGCApplicationConcurrentTime -XX:+PrintGCApplicationStoppedTime BigObject,输出结果如下:
Application time: 0.5114944 seconds
[GC [DefNew: 3968K->64K(4032K), 0.0823952 secs] 3968K->2023K(32704K), 0.0827626 secs]
Total time for which application threads were stopped: 0.0839428 seconds
年轻代的Minor收集占用的时间比率计算如下:
应用线程被中断的总时长/(应用执行总时长 + 应用线程被中断的总时长)。
那么在本例中垃圾收集占用的时间比率为:0.0839428 /(0.5114944 + 0.0839428 ) = 14%;
垃圾收集占用的时间的比率越大,系统的响应越慢。
优化提示:适当提高年轻代的大小;改变收集方式。
4. 如何缩短minor收集暂停时间?运行 java -XX:+PrintGCDetails -XX:+UseParNewGC BigObject。
5. 如何缩短major收集暂停时间?运行 java -XX:+PrintGCDetails -XX:+UseConcMarkSweepGC BigObject
6. 如何缩短收集暂停时间?运行 java -XX:+PrintGCDetails -XX:+UseConcMarkSweepGC -XX:+UseParNewGC BigObject
7. 如何提高GC的吞吐量?
机器配置:4G的内存,32个CPU。
java -Xmx3200m -Xms3200m -Xmn2g -Xss128k -XX:+UseParallelGC -XX:ParallelGCThreads=20,说明如下:
(1)-Xmx3200m -Xms3200m 配置了最大Heap来充分利用系统内存。
(2)-Xmn2g 创建足够大的年轻代,防止将短期对象复制到年老代。
(3)-Xss128 减少默认最大的线程栈大小,提供更多的处理虚拟内存地址空间被进程使用。
(4)-XX:+UseParallelGC 采用并行垃圾收集器对年青代的内存进行收集,提高效率。
(5)-XX:ParallelGCThreads=20 减少垃圾收集线程,默认是和CPU个数相同,往往不需要配置到最大值。
8. 采用对年老代并行收集,JDK1.6支持。机器配置:4G的内存,32个CPU。
java -Xmx3000m -Xms3000m -Xmn2g -Xss128k -XX:+UseParallelGC -XX:ParallelGCThreads=20 -XX:+UseParallelOldGC,说明如下:
(1)-Xmx3000m -Xms3000m 内存分配被减小,因为ParallelOldGC会增加对于Native Heap的需求,因此需要减小Java Heap来满足需求。
(2)-XX:+UseParallelOldGC 采用对于老年代并发收集的策略,提高收集效率。
9. 既提高吞吐量,又减少应用暂停时间
机器配置:4G的内存,32个CPU。
java -Xmx3550m -Xms3550m -Xmn2g -Xss128k -XX:ParallelGCThreads=20 -XX:+UseConcMarkSweepGC -XX:+UseParNewGC -XX:SurvivorRatio=8 -XX:TargetSurvivorRatio=90 -XX:MaxTenuringThreshold=31
(1)-XX:+UseConcMarkSweepGC -XX:+UseParNewGC 选择了并发标记交换收集器,它可以并发执行收集操作,降低应用暂停时间,同时它也是并行处理模式,有效地利用多CPU的处理能力。
(2)-XX:SurvivorRatio=8 年轻代中Eden和Survivor比例,两个Survivor区与一个Eden区的比值为2:8。Survivor越大,短期对象在年轻代被回收的可能性越高。
(3)-XX:MaxTenuringThreshold=31 熬过年轻代31次收集后才进入年老代,提高短期对象在年轻代被回收的可能性。
(4)-XX:TargetSurvivorRatio=90 允许使用90%的Survivor区空间,超过默认的50%,提高Survivor区的使用率。
10. 当系统负载与GC“共振”时,如何解决?在《优化Java 垃圾收集器改进系统性能》这篇文章中,任务数与GC发生“共振”:任务数比较高时,JVM开始GC,导致更多的任务无法被处理。通过减少Heap的最大值,降低每次GC所花的时间,提高GC的次数,使其与任务数的频率不一致。从而使CPU有时间可以处理更多的任务。
参考文献:
1. 《JVM调优》。
没有评论:
发表评论