java directmemory gc_关于DirectMemory和ExplicitGCInvokesConcurrent

本文章为各位介绍一下关于DirectMemory和ExplicitGCInvokesConcurrent的使用方法及一些注意事项了,希望文章能够帮助到各位朋友。

其实是去年的一个bug,就是线上出现了一次java.lang.OutOfMemoryError: Direct buffer memory。

原因,从Direct Memory的回收机制说起:

我们的系统使用了一个基于nio的消息队列,而direct memory是放在堆外的,堆内放的时direct memory的引用。正常情况下当在young gc 的时候会把这个已死的引用回收,进而回收direct memory 区域。但是如果内存引用被移到old gen中时,又没有发生Full GC,那么direct memory就会一直累积不释放。最后导致直接内存OOM。其实这样也不会有问题,问题是我们在JVM中加入了-XX: DisableExplicitGC,很多jvm调优的建议参数。真正原因是:DirectByteBuffer分配空间过程中会显式调用System.gc(),以期通过Full GC来强迫已经无用的DirectByteBuffer对象释放掉它们关联的native memory。

在分配DirectByteBuffer时,会调用java.nio.Bits.reserveMemory(size, cap)方法:

 代码如下复制代码

static void reserveMemory(long size, int cap){

if(!memoryLimitSet && VM.isBooted()){

maxMemory = VM.maxDirectMemory();

memoryLimitSet = true;

}

// optimist!

if(tryReserveMemory(size, cap)){

return;

}

final JavaLangRefAccess jlra = SharedSecrets.getJavaLangRefAccess();

// retry while helping enqueue pending Reference objects

// which includes executing pending Cleaner(s) which includes

// Cleaner(s) that free direct buffer memory

while(jlra.tryHandlePendingReference()){

if(tryReserveMemory(size, cap)){

return;

}

}

// trigger VM's Reference processing

System.gc();

// a retry loop with exponential back-off delays

// (this gives VM some time to do it's job)

boolean interrupted = false;

try{

long sleepTime = 1;

int sleeps = 0;

while(true){

if(tryReserveMemory(size, cap)){

return;

}

if(sleeps >= MAX_SLEEPS){

break;

}

if(!jlra.tryHandlePendingReference()){

try{

Thread.sleep(sleepTime);

sleepTime <<= 1;

sleeps ;

}catch(InterruptedException e){

interrupted = true;

}

}

}

// no luck

throw new OutOfMemoryError("Direct buffer memory");

}finally{

if(interrupted){

// don't swallow interrupts

Thread.currentThread().interrupt();

}

}

}

在tryReserveMemory中计算剩余容量是否可以分配指定大小的直接内存,如果可以直接分配,否则执行System.gc()来回收直接内存,当System.gc()被禁用就会throw new OutOfMemoryError。

解决方式:

去掉-XX: DisableExplicitGC,但是nio框架中频繁调用System.gc会严重影响性能。其实,这个有更好的解决办法,使用-XX: ExplicitGCInvokesConcurrent 或 -XX: ExplicitGCInvokesConcurrentAndUnloadsClasses参数。和-XX: DisableExplicitGC一样,这两个参数也是用来改变System.gc()的默认行为用的;不同的 是这两个参数只能配合CMS使用(-XX: UseConcMarkSweepGC),而且System.gc()还是会触发GC的,只不过不是触发一个 完全stop-the-world的full GC,而是一次并发GC周期(注:一次并发周期其实就是在CMS下的一次gc,CMS只能用在full gc 中,所以,也是一次full gc 只不过效率比较高)。

总结:

当程序中出现:

使用了NIO或者NIO框架(Mina/Netty等)

使用了DirectByteBuffer分配字节缓冲区

使用了MappedByteBuffer做内存映射

最好使用ExplicitGCInvokesConcurrent参数来替代DisableExplicitGC。需要注意的是ExplicitGCInvokesConcurrent只能配合CMS收集器使用。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值