下面查看ByteBuffer类的源码来验证直接内存分配、释放的的过程。
allocateDirect()中返回一个DirectByteBuffer对象。
public static ByteBuffer allocateDirect(int capacity) { return new DirectByteBuffer(capacity); }
调用Unsafe中allocateMemory()来实现申请内存,新建Cleaner对象来释放内存。
DirectByteBuffer(int cap) { // package-private 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; }
cleaner中关联的Deallocator是什么?点进去看发现它实现了Runnable,是回调任务对象,在run方法中调用了Unsafe的freeMemory。
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); } }
那么垃圾回收的任务什么时候被执行的呢?看Cleaner源码。
public class Cleaner extends PhantomReference<Object> { //... public void clean() { if (!remove(this)) return; try { thunk.run(); } catch (final Throwable x) { AccessController.doPrivileged(new PrivilegedAction<Void>() { public Void run() { if (System.err != null) new Error("Cleaner terminated abnormally", x) .printStackTrace(); System.exit(1); return null; }}); } } //... }
原来Cleaner是java中的虚引用类型,当它的绑定的对象被垃圾回收时,会触发虚引用的clean()方法,执行回调方法run()。
下面回过头看DirectByteBuffer类中的Cleaner创建,过程就清楚了。
cleaner = Cleaner.create(this, new Deallocator(base, size, cap));
总结直接内存分配、释放的的过程就是:通过调用Unsafe的allocateMemory来分配直接内存,通过创建虚引用对象Cleaner对象,将DirectoryByteBuffer与回调任务绑定,当Directory被垃圾回收时,会自动执行Cleaner的clean()方法,来调用Unsafe的freeMemory()释放内存。