CMSGC造成内存碎片的解决方法
我们知道,CMSGC在老生代回收时产生的内存碎片会导致老生代的利用率变低;或者可能在老生代总内存大小足够的情况下,却不能容纳新生代的晋升行为(由于没有连续的内存空间可用),导致触发FullGC。针对这个问题,Sun官方给出了以下的四种解决方法:
- 增大Xmx或者减少Xmn
- 在应用访问量最低的时候,在程序中主动调用System.gc(),比如每天凌晨。
- 在应用启动并完成所有初始化工作后,主动调用System.gc(),它可以将初始化的数据压缩到一个单独的chunk中,以腾出更多的连续内存空间给新生代晋升使用。
- 降低-XX:CMSInitiatingOccupancyFraction参数以提早执行CMSGC动作,虽然CMSGC不会进行内存碎片的压缩整理,但它会合并老生代中相邻的free空间。这样就可以容纳更多的新生代晋升行为。
原文引用:
引用
What should you do if you run into a fragmentation problem?
Try 5.0.
Or you could try a larger total heap and/or smaller young generation. If your application is on the edge, it might give you just enough extra space to fit all your live data. But often it just delays the problem.
Or you can try to make you application do a full, compacting collection at a time which will not disturb your users. If your application can go for a day without hitting a fragmentation problem, try a System.gc() in the middle of the night. That will compact the heap and you can hopefully go another day without hitting the fragmentation problem. Clearly no help for an application that does not have a logical "middle of the night".
Or if by chance most of the data in the tenured generation is read in when your application first starts up and you can do a System.gc() after you complete initialization, that might help by compacting all data into a single chunk leaving the rest of the tenured generation available for promotions. Depending on the allocation pattern of the application, that might be adequate.
Or you might want to start the concurrent collections earlier. The low pause collector tries to start a concurrent collection just in time (with some safety factor) to collect the tenured generation before it is full. If you are doing concurrent collections and freeing enough space, you can try starting a concurrent collection sooner so that it finishes before the fragmentation becomes a problem. The concurrent collections don't do a compaction, but they do coalese adjacent free blocks so larger chunks of free space can result from a concurrent collection. One of the triggers for starting a concurrent collection is the amount of free space in the tenured generation. You can cause a concurrent collection to occur early by setting the option -XX:CMSInitiatingOccupancyFraction= where NNN is the percentage of the tenured generation that is in use above which a concurrent collection is started. This will increase the overall time you spend doing GC but may avoid the fragmentation problem. And this will be more effective with 5.0 because a single contiguous chunk of space is not required for promotions.
Try 5.0.
Or you could try a larger total heap and/or smaller young generation. If your application is on the edge, it might give you just enough extra space to fit all your live data. But often it just delays the problem.
Or you can try to make you application do a full, compacting collection at a time which will not disturb your users. If your application can go for a day without hitting a fragmentation problem, try a System.gc() in the middle of the night. That will compact the heap and you can hopefully go another day without hitting the fragmentation problem. Clearly no help for an application that does not have a logical "middle of the night".
Or if by chance most of the data in the tenured generation is read in when your application first starts up and you can do a System.gc() after you complete initialization, that might help by compacting all data into a single chunk leaving the rest of the tenured generation available for promotions. Depending on the allocation pattern of the application, that might be adequate.
Or you might want to start the concurrent collections earlier. The low pause collector tries to start a concurrent collection just in time (with some safety factor) to collect the tenured generation before it is full. If you are doing concurrent collections and freeing enough space, you can try starting a concurrent collection sooner so that it finishes before the fragmentation becomes a problem. The concurrent collections don't do a compaction, but they do coalese adjacent free blocks so larger chunks of free space can result from a concurrent collection. One of the triggers for starting a concurrent collection is the amount of free space in the tenured generation. You can cause a concurrent collection to occur early by setting the option -XX:CMSInitiatingOccupancyFraction= where NNN is the percentage of the tenured generation that is in use above which a concurrent collection is started. This will increase the overall time you spend doing GC but may avoid the fragmentation problem. And this will be more effective with 5.0 because a single contiguous chunk of space is not required for promotions.
我在实际应用中的调优是:
- 在应用启动并完成所有初始化工作后,主动调用System.gc()。
- 在Xmx不变的情况下,保持SuvivorSpace不变(为了不让每次MinorGC的晋升大小增加),降低Xmn。
- 降低-XX:CMSInitiatingOccupancyFraction
我认为在程序中调用System.gc()并不是一个很好的选择,因为:
- FullGC一定会导致应用暂停,而有些高并发应用是不允许有一次FullGC的,so...
- 在jdk1.6的某个版本中,调用System.gc()并且应用中有nio操作的话,会导致应用挂起的Bug
另外的一种方法是(没用过,仅供参考):
http://kenwublog.com/avoid-full-gc-in-hbase-using-arena-allocation