Contents

JAVA幻象引用与NIO的直接内存释放

JAVA幻象引用

最近详细看了JAVA四种引用模式的机制,其中有个幻象引用,它无法获取到具体的对象值,那问题来了,这玩意到底有什么用呢?

幻象引用:没有强、软、弱引用可达,并且已经finalize过了,只有幻象引用只想这个对象。

引用队列:当GC释放相应当关联引用队列的引用所指向的对象时,会将引用push进队列,作为一种通知,方便后续操作。

NIO的DirectByteBuffer如何释放堆外内存

众所周知,在netty中我们尽可能使用PooledUnsafeDirectByteBuf去复用堆外的直接内存,原因就是避免创建和销毁的开销,统一管理。 那么既然堆外内存不在Heap内管理,它如何释放呢?下面摘自Stackoverflow (链接1):

When you are calling the JNI:
NewDirectByteBuffer(void* address, jlong capacity)
a DirectByteBuffer object will be created, using
the following constructor:
    private DirectByteBuffer(long addr, int cap) {
      super(-1, 0, cap, cap);
      address = addr;
      cleaner = null;
      att = null;
    };
Notice that the cleaner property is null.

If you create a DirectByteBuffer via
ByteBuffer.allocateDirect() the cleaner property is
set (see source code of DirectByteBuffer).

Now the cleaner is a utility object whose clean
method is run when the buffer is garbage collected
(see this explanation for details).

For the buffer constructed via
ByteBuffer.allocateDirect() the cleaner deallocates
the direct memory.

For the JNI constructed buffer no such operation is
done. You need to free this memory yourself.

clear的触发机制(链接2):

In the Sun JDK, a java.nio.DirectByteBuffer—created
by ByteBuffer#allocateDirect(int)—has a field of
type sun.misc.Cleaner, which extends
java.lang.ref.PhantomReference.

When this Cleaner (remember, a subtype of
PhantomReference) gets collected and is about to move
into the associated ReferenceQueue, the
collection-related thread running through the nested
type ReferenceHandler has a special case treatment of
Cleaner instances: it downcasts and calls on
Cleaner#clean(), which eventually makes its way back
to calling on DirectByteBuffer$Deallocator#run(), which
in turn calls on Unsafe#freeMemory(long). Wow.

It's rather circuitous, and I was surprised to not see
any use of Object#finalize() in play. The Sun developers
must have had their reasons for tying this in even closer
to the collection and reference management subsystem.

In short, you won't run out of memory by virtue of
abandoning references to DirectByteBuffer instances,
so long as the garbage collector has a chance to
notice the abandonment and its reference handling
thread makes progress through the calls described
above.

主要的意思就是: 1)DirectBuffer通过一个Cleaner来进行回收;

2)Cleaner本质上是PhantomReference 附带一个ReferenceQueue,它管理buffer的堆外的内存地址;

3)一旦DirectBuffer的堆内描述对象的强引用没了,导致它的Cleaner这个PhantomReference属性被GC注意,Cleaner这个PhantomReference引用被push到Queue中,回收线程会发起Cleaner调Deallocator里的Unsafe#freeMemory(long)这个Native回收方法了。

总结

所以,Phantom Reference的作用就是用来在准确的时机做资源回收通知的,并需要配合 ReferenceQueue才行。