最大堆外内存的配置
-XX:MaxDirectMemorySize=15g
分配堆外内存
java.nio.ByteBuffer#allocateDirect
DirectByteBuffer 类是包权限的,使用 unsafe 分配和回收内存
class DirectByteBuffer extends MappedByteBuffer implements DirectBuffer DirectByteBuffer(int cap) { super(-1, 0, cap, cap); boolean pa = VM.isDirectMemoryPageAligned(); int ps = Bits.pageSize(); long size = Math.max(1L, (long)cap + (pa ? ps : 0)); Bits.reserveMemory(size, cap); long base = 0; try { base = unsafe.allocateMemory(size); } catch (OutOfMemoryError x) { Bits.unreserveMemory(size, cap); throw x; } unsafe.setMemory(base, size, (byte) 0); if (pa && (base % ps != 0)) { // Round up to page boundary address = base + ps - (base & (ps - 1)); } else { address = base; } cleaner = Cleaner.create(this, new Deallocator(base, size, cap)); att = null; } private static class Deallocator implements Runnable { private static Unsafe unsafe = Unsafe.getUnsafe(); private long address; private long size; private int capacity; private Deallocator(long address, long size, int capacity) { assert (address != 0); this.address = address; this.size = size; this.capacity = capacity; } public void run() { if (address == 0) { // Paranoia return; } unsafe.freeMemory(address); address = 0; Bits.unreserveMemory(size, capacity); } } private final Cleaner cleaner; }
堆外内存的回收,也受 GC 控制,最终也是调用了 cleaner 的 clean 方法,然后到 Deallocator 的 run 方法、
所以,可以通过反射来主动调用该方法,释放堆外内存。
rocketMQ 释放 mmap 产生的堆外内存就是这么做的。