【Java】 DirectByteBuffer堆外内存回收

PhantomReference虚引用在分析堆外内存回收之前,先了解下PhantomReference虚引用 。
PhantomReference需要与ReferenceQueue引用队列结合使用,在GC进行垃圾回收的时候 , 如果发现一个对象只有虚引用在引用它,则认为该对象需要被回收,会将引用该对象的虚引用加入到与其关联的ReferenceQueue队列中 , 开发者可以通过ReferenceQueue获取需要被回收的对象,然后做一些清理操作,从队列中获取过的元素会从队列中清除 , 之后GC就可以对该对象进行回收 。
虚引用提供了一种追踪对象垃圾回收状态的机制,让开发者知道哪些对象准备进行回收,在回收之前开发者可以进行一些清理工作,之后GC就可以将对象进行真正的回收 。
来看一个虚引用的使用例子:

  1. 创建一个ReferenceQueue队列queue,用于跟踪对象的回收;
  2. 创建一个obj对象,通过new创建的是强引用,只要强引用存在,对象就不会被回收;
  3. 创建一个虚引用PhantomReference,将obj对象和ReferenceQueue队列传入,此时phantomReference里面引用了obj对象,并关联着引用队列queue;
  4. 同样的方式创建另一个obj1对象和虚引用对象phantomReference1;
  5. 将obj置为NULL,此时强引用关系失效;
  6. 调用 System.gc()进行垃圾回收;
  7. 由于obj的强引用关系失效 , 所以GC认为该对象需要被回收 , 会将引用该对象的虚引用phantomReference对象放入到与其关联的引用队列queue中;
  8. 通过poll从引用队列queue中获取对象,可以发现会获取到phantomReference对象,poll获取之后会将对象从引用队列中删除 , 之后会被垃圾回收器回收;
  9. obj1的强引用关系还在,所以从queue中并不会获取到;
public static void main(String[] args) {// 创建引用队列ReferenceQueue<Object> queue = new ReferenceQueue<Object>();// 创建obj对象Object obj = new Object();// 创建虚引用,虚引用引用了obj对象,并与queue进行关联PhantomReference<Object> phantomReference = new PhantomReference<Object>(obj, queue);// 创建obj1对象Object obj1 = new Object();PhantomReference<Object> phantomReference1 = new PhantomReference<Object>(obj1, queue);// 将obj置为NULL,强引用关系失效obj = null;// 垃圾回收System.gc();// 从引用队列获取对象Object o = queue.poll();if (null != o) {System.out.println(o.toString());}}输出结果:
java.lang.ref.PhantomReference@277c0f21
【Java】 DirectByteBuffer堆外内存回收

文章插图
Reference实例的几种状态
Active:初始状态,创建一个Reference类型的实例之后处于Active状态,以上面虚引用为例,通过new创建一个PhantomReference虚引用对象之后,虚引用对象就处于Active状态 。
Pending:当GC检测到对象的可达性发生变化时,会根据是否关联了引用队列来决定是否将状态更改为Pending或者Inactive,虚引用必须与引用队列结合使用,所以对于虚引用来说,如果它实际引用的对象需要被回收,垃圾回收器会将这个虚引用对象加入到一个Pending列表中,此时处于Pending状态 。
同样以上面的的虚引用为例,因为obj的强引用关系失效,GC就会把引用它的虚引用对象放入到pending列表中 。
Enqueued:表示引用对象被加入到了引用队列,Reference有一个后台线程去检测是否有处于Pending状态的引用对象 , 如果有会将引用对象加入到与其关联的引用队列中,此时由Pending转为Enqueued状态表示对象已加入到引用队列中 。
Inactive:通过引用队列的poll方法可以从引用队列中获取引用对象,同时引用对象会从队列中移除,此时引用对象处于Inactive状态,之后会被GC回收 。
DirectByteBuffer堆外内存回收在DirectByteBuffer的构造函数中,在申请内存之前,先调用了BitsreserveMemory方法回收内存,申请内存之后,调用Cleanercreate方法创建了一个Cleaner对象 , 并传入了当前对象(DirectByteBuffer)和一个Deallocator类型的对象:
class DirectByteBuffer extends MappedByteBuffer implements DirectBuffer {private final Cleaner cleaner;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 boundaryaddress = base + ps - (base & (ps - 1));} else {address = base;}// 创建Cleader,传入了当前对象和Deallocatorcleaner = Cleaner.create(this, new Deallocator(base, size, cap));att = null;}}

推荐阅读