-XX:+DisableExplicitGC弊端
总结:如果jvm参数中设置了-XX:+DisableExplicitGC,那么代码中手动调用System.gc()就不会生效。而有些框架中因为是使用的堆外内存,必须手动调用System.gc()来释放。如果禁用掉就会导致堆外内存使用一直增长,造成内存泄露。详解:直接内存与System.gc()System.gc()默认会触发一次Full GC,如果在代码中不小心调用了Syst...
总结:
如果jvm参数中设置了-XX:+DisableExplicitGC,那么代码中手动调用System.gc()就不会生效。而有些框架中因为是使用的堆外内存,必须手动调用System.gc()来释放。如果禁用掉就会导致堆外内存使用一直增长,造成内存泄露。
详解:
直接内存与System.gc()
System.gc()默认会触发一次Full GC,如果在代码中不小心调用了System.gc()会导致JVM间歇性的暂停,但有些NIO框架
比如Netty框架经常会使用DirectByteBuffer来分配堆外内存,在分配之前会显式的调用System.gc(),如果开启了DisableExplicitGC
这个参数,会导致System.gc()调用变成一个空调用,没有任何作用,反而会导致Netty框架无法申请到足够的堆外内存,从而产生
java.lang.OutOfMemoryError: Direct buffer memory
既然是堆外内存,为什么触发Full GC会有助于回收堆外内存呢,Full GC不是只回收JVM的堆内存吗?这就要了解下DirectByteBuffer的回收机制了,DirectByteBuffer没有finalizer,它的native memory的清理工作是通过sun.misc.Cleaner自动完成的,而sum.misc.Cleaner是一种基于虚引用的回收工具,从JDK源码也可以看到:
public class Cleaner extends PhantomReference<Object>
当GC检查到Cleaner的引用变成虚引用可达时,reference-handler线程会调用Cleaner的clean方法回收内存,这个机制可以在
java.lang.ref.Reference$ReferenceHandler里看到,Reference类加载的时候会创建reference-handler线程:
public void run() {
for (;;) {
//省略了一些
// Fast path for cleaners
if (r instanceof Cleaner) {
((Cleaner)r).clean();
continue;
}
}
static {
ThreadGroup tg = Thread.currentThread().getThreadGroup();
for (ThreadGroup tgn = tg;
tgn != null;
tg = tgn, tgn = tg.getParent());
Thread handler = new ReferenceHandler(tg, "Reference Handler");
/* If there were a special system-only priority greater than
* MAX_PRIORITY, it would be used here
*/
handler.setPriority(Thread.MAX_PRIORITY);
handler.setDaemon(true);
handler.start();
}
JVM在做Full GC时会对引用作处理(reference processing),当GC检测到Cleaner的引用变成虚可达时,引用Handler线程会触发Cleaner对DirectByteBuffer对象作清理工作.
CMS垃圾回收器下的推荐配置
既然不推荐使用DisableExplicitGC这个参数,那有没有什么办法能尽量减少显式调用System.gc()带来的GC停顿呢,JVM提供了
ExplicitGCInvokesConcurrent和ExplicitGCInvokesConcurrentAndUnloadsClasses这两个参数来保证显式调用System.gc()
触发的是一个并发GC周期而不是Full GC,这两个参数只能配合CMS使用(-XX:+UseConcMarkSweepGC):
CMS GC周期内也会做reference-processing,因此也能够触发对DirectByteBuffer内存的回收,减少了Full GC带来的长时间
停顿。
当没有开启DisableExplicitGC这个参数时,你会发现JVM每个小时会执行一次Full GC,这是因为JVM在做分布式GC,为RMI服务的,
可以通过sun.rmi.dgc.server.gcInterval这个参数来修改GC间隔,默认是一个小时。
System.gc常识
- system.gc其实是做一次full gc
- system.gc会暂停整个进程
- system.gc一般情况下我们要禁掉,使用-XX:+DisableExplicitGC
- system.gc在cms gc下我们通过-XX:+ExplicitGCInvokesConcurrent来做一次稍微高效点的GC(效果比Full GC要好些)
- system.gc最常见的场景是RMI/NIO下的堆外内存分配等
参考:http://docs.oracle.com/javase/6/docs/technotes/guides/rmi/sunrmiproperties.html
开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!
更多推荐
所有评论(0)