一、概述
ByteBuffer
属于 java.nio
包下的类,它是 Java 提供的堆外内存的 Buffer,主要用于 Java NIO 编程中。
二、源码
DirectByteBuffer(int cap) {
super(-1, 0, cap, cap, null);
boolean pa = VM.isDirectMemoryPageAligned();
int ps = Bits.pageSize();
long size = Math.max(1L, (long)cap + (pa ? ps : 0));
// 强制执行 Full GC,但一般生产环境都是设置了 -XX:DisableExplicitGC,所以是不起作用的
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 对象,负责堆外内存回收工作。
// 虚引用(PhantomReference)和 ReferenceQueue 配合使用达到回收堆外内存的目的
// Deallocator 内部持有堆外内存基地址以及大小和容量,并有一个 run方法用以释放堆外内存
// Cleaner 是一个双端链表,create包含add方法,添加到首部位置
// 当 堆内的 DirectByteBuffer 对象被回收,同时也表示其指向的堆外内存也需要被回收,
// 这时,Cleaner对象不再有任何引用关系,在下一次 GC 时,该 Cleaner对象将被添加到 ReferenceQueue
// 中并执行 clean() 方法,它主要做两件事件
// 1.将 Cleaner 对象从链表中移除
// 2.调用 UNSAFE.freeMemory(address); 方法清理堆外内存
cleaner = Cleaner.create(this, new Deallocator(base, size, cap));
att = null;
}
private static class Deallocator implements Runnable {
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);
}
}