驽马十驾 驽马十驾

驽马十驾,功在不舍

目录
关于堆外内存清理的记录
/    

关于堆外内存清理的记录

开篇

前几篇文章有提到Netty的零拷贝中其中有一个体现是使用了NIODirectByteBuffer,它是堆外内存,不会占据堆内存,那么这块地域会被垃圾回收期回收吗?

这篇文章,我们就来聊聊关于堆外内存的垃圾回收工作。

正文

手动清理

我们先来看看堆外内存中有关垃圾清理的关键Cleaner,下面代码是相关源代码,经过精简!

class DirectByteBuffer extends MappedByteBuffer implements DirectBuffer {
  //清理器
	private final Cleaner cleaner;
  
	DirectByteBuffer(int cap) { // package-private
    //确保能保留一定内存
    Bits.reserveMemory(size, cap);
		//创建清理器
		cleaner = Cleaner.create(this, new Deallocator(base, size, cap));
	}
}

很明显在DirectByteBuffer创建的时候,是有一个Cleaner的,而它就是清理工作的关键所在,下面这段代码就是利用这个Cleaner来进行堆外内存的清理工作

public class DirectByteBufferCleaner {
        public static void clean(final ByteBuffer byteBuffer) {
              if (byteBuffer.isDirect()) {
                 ((DirectBuffer)byteBuffer).cleaner().clean();
              }
        }
}

不过假如用户不通过Cleaner来进行清理

GC清理

除了手动清理,GC会给我们清理吗,答案是会的,我们来做个测试,有如下代码,跑在JDK8下。

/**
* VM参数1: -XX:+PrintGC  -XX:MaxDirectMemorySize=30m -XX:+PrintGCDetails
* VM参数2: VM参数1 + -XX:+DisableExplicitGC 
*/
public static void TestDirectByteBuffer() {
	while (true) {
		ByteBuffer buffer = ByteBuffer.allocateDirect(10 * 1024 * 1024);
	}
}

在参数1下,通过MaxDirectMemorySize=40M, 设置了最大堆外空间是,然后执行一段时间,发现没有发生OOM

在参数2下,新增了-XX:+DisableExplicitGC,其意思是不允许System.gc()工作,此时运行一段时间,OOM发生。

要了解原因,还是要看源码的。上文中第一部分源码展示了在创建DirectByteBuffer的时候,有这么一句代码:Bits.reserveMemory(size, cap);,现在我们来看看它的实现:

static void reserveMemory(long size, int cap) {
        //....
        // optimist! 积极的尝试保证所需的内存
        if (tryReserveMemory(size, cap)) {
            return;
        }
        final JavaLangRefAccess jlra = SharedSecrets.getJavaLangRefAccess();
        while (jlra.tryHandlePendingReference()) {
            //再保证一次
            if (tryReserveMemory(size, cap)) {
                return;
            }
        }
     	 //上面的几个步骤如果没有保证了,那么只能调用System.gc()了
       System.gc();

上面代码清楚的展示了当我们创建堆外内存的时候,JVM会给我们去尝试获取要求大小的堆外内存,当无法获取到这么大空间的时候,会调用System.gc()来进行FullGC从而释放堆外内存。

结语

综上,堆外内存的清理工作既可以手动调用Cleaner进行清理,也可以交给Full GC来做!不过非常建议采取前者来进行堆外内存的回收!

同时再使用涉及使用堆外内存的框架的时候(Netty),请一定不要在启动参数中添加-XX:+DisableExplicitGC.

骐骥一跃,不能十步。驽马十驾,功在不舍。