概述
使用了nio框架的应用,比如服务框架,利用nio建立长连接通信,他们会使用DirectByteBuffer来分配堆外内存,也就是本地直接内存,这个内存的回收不由gc直接维护,我们通常所说的gc,只回收jvm的堆、方法区。本地内存如果没有用jvm启动参数手动指定,它会根据主机的剩余可用内存进行分配,如果说一个机器的8G内存的,其中,我们手动指定的jvm堆、方法区内存为2048 + 256,那么,除了其他进程占用的内存,剩余的可用内存可能是较大的。如果你的主机有内存使用量监控(不是jvm级的内存监控),在使用类似Netty这种通信框架时,有可能会触发主机内存使用率报警。
堆外内存回收方法
-
首先强调,gc不会直接回收堆外内存,堆外内存如果不通过启动参数指定,会根据主机的剩余可用内存来作为容量,这有可能是一块很大的内存,gc回收代价可能较大
-
DirectByteBuffer对象在堆内生成时,会和一个RefereceQueue建立虚引用联系,这里是通过Cleaner对象的某个field被赋值为DirectByteBuffer对象来建立虚引用的,注意,这里是Cleaner对象虚引用了DirectByteBuffer对象,引用queue为cleaner中的dummyQueue。再次强调,cleaner对象虽然虚引用了DirectByteBuffer,但是垃圾回收在计算对象可达性时,会忽略Cleaner对的DirectByteBuffer虚引用,DirectByteBuffer只有可以,就可以立即回收,无视Cleaner对象当前是存活状态。
-
jdk实现GC时,对几种引用类型有定制化开发,在对象B被回收后,会通知到一个ReferenceHandler线程,获取到虚引用对象A,判断A是否是Cleaner,如果是就会调用Cleaner.clean()方法,获取DirectByteBuffer被分配的堆外内存地址,释放B在堆外内存开辟的空间,释放内存
class DirectByteBuffer{
// Primary constructor
//
DirectByteBuffer(int cap) { // package-privatesuper(-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);
}}
...
}
Clean类,runnable这里对应Deallocator类型
package sun.misc;
import java.lang.ref.PhantomReference;
import java.lang.ref.ReferenceQueue;
import java.security.AccessController;
import java.security.PrivilegedAction;
public class Cleaner extends PhantomReference
浙公网安备 33010602011771号