JVM堆以外的内存,这些内存直接受操作系统管理(而不是虚拟机)。
使用未公开的Unsafe和NIO包下ByteBuffer来创建堆外内存。
优势
-
减少了垃圾回收对应用程序造成的影响
-
提升复制速度(io效率),堆外内存的数据可直接由操作系统写入磁盘。
堆内内存由JVM管理,属于“用户态”;
堆外内存由OS管理,属于“内核态”。
如果从堆内向磁盘写数据时,数据会先被复制到堆外内存,即内核缓冲区,然后再由OS写入磁盘,使用堆外内存避免了这个操作。
堆外内存的申请
ByteBuffer.allocateDirect(1024)
源码:
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); //用Bits类保存总分配内存(按页分配)的大小和实际内存的大小
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)); // Cleaner
att = null;
}
堆外内存释放
把自身从Clener链表删除,从而在下次GC时能够被回收(基于可达性分析)。如果FGC不主动触发,System.gc()主动触发。
((DirectBuffer) byteBuffer).cleaner().clean();