每当应用程序使用G1收集算法时,GC时对大对象的分配可能会影响应用程序的性能(强调:大对象分配是指大于region大小的50%对象的分配)。
频繁地大对象分配会引发一下GC性能问题:
- 如果region当中包含大对象,剩余regiong空间将不再被使用,如果对象只是刚刚超过region的50%多一点,那么就会造成很大的空间浪费。
- 对于大对象的收集,G1并不像常规那样处理,JAVA1.8u40之前,大对象的回收只能在Full GC事件期间完成,Hotspot JVM 最新版本会在清除阶段标记周期结束时释放大区域,对于轿新的JVM来说,问题的影响已经显著下降。
要检查应用是否存在大对象空间需要制定JVM参数:
java -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:+PrintReferenceGC -XX:+UseG1GC -XX:+PrintAdaptiveSizePolicy -Xmx128m MyClass
G1出现大对象的日志:
0.106: [G1Ergonomics (Concurrent Cycles) request concurrent cycle initiation, reason: occupancy higher than threshold, occupancy: 60817408 bytes, allocation request: 1048592 bytes, threshold: 60397965 bytes (45.00 %), source: concurrent humongous allocation]
0.106: [G1Ergonomics (Concurrent Cycles) request concurrent cycle initiation, reason: requested by GC cause, GC cause: G1 Humongous Allocation]
0.106: [G1Ergonomics (Concurrent Cycles) initiate concurrent cycle, reason: concurrent cycle initiation requested]
0.106: [GC pause (G1 Humongous Allocation) (young) (initial-mark) 0.106: [G1Ergonomics (CSet Construction) start choosing CSet, _pending_cards: 0, predicted base time: 10.00 ms, remaining time: 190.00 ms, target pause time: 200.00 ms]
你现在有证据证明应用程序确实在分配大对象GC pause (G1 Humongous Allocation),大对象请求空间为1048592字节(1M多点),region默认大小是2M(2097152 Bytes),计算得到:16 Bytes = 2097152 * 0.5 - 1048592 ; 大对象只超了16 Bytes
解决方案:
- 指定JVM参数**-XX:G1HeapRegionSize=XX**,增大region的空间来覆盖大小,指定范围在1~32M之间
- 有副作用,增大region的大小,将减少整个region的个数,修改之后需要查看是否提高了应用程序的吞吐量和降低了应用程序的延迟
- 另一种解决方案是修改应用程序,尽量避免大对象的分配,但是操作起来工作量就比较大
结论:
JVM之上可能存在运行多个应用程序,加上GC调整的参数上百个,GC性能优化的方面就很多。
参考《Plumbr Handbook Java》