最大堆外內存的配置
-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 產生的堆外內存就是這么做的。