堆外内存

原文:http://lovestblog.cn/blog/2015/05/12/direct-buffer/

这里只截取自己要的

广义的堆外内存:新生代、老生代、持久代之外的内存,包括jvm本身在运行过程中分配的内存,jni里分配的内存,以及下面提到的DirectByteBuffer分配的内存等等

狭义的堆外内存:主要指java.nio.DirectByteBuffer在创建时分配的内存,本文也指的是狭义的堆外内存

System.gc

堆外内存不会对gc造成什么影响(System.gc除外),堆外内存的回收其实依赖于我们的gc机制。首先在java层面和这块堆外内存关联的只有与之关联的DirectByteBuffer对象,它记录了这块内存的基地址以及大小,gc能通过操作DirectByteBuffer对象来间接操作对应的堆外内存。

DirectByteBuffer对象在创建的时候关联了一个PhantomReference,PhantomReference是用于跟踪对象何时被回收的,它不能影响gc决策,但是gc过程中如果返现某个对象除了PhantomReference外没有其他地方引用,就会将这个引用java.lang.ref.Reference.pending队列里,在gc完毕时通知ReferenceHandler这个守护线程去执行一些后置处理,而DirectByteBuffer关联的PhantomReference是PhantomReference的一个子类,在最终处理里会通过Unsafe的free接口来释放DirectByteBuffer对应的对外内存块

JDK里ReferenceHandler实现:

private static class ReferenceHandler extends Thread {

        ReferenceHandler(ThreadGroup g, String name) {
            super(g, name);
        }

        public void run() {
            for (;;) {

                Reference r;
                synchronized (lock) {
                    if (pending != null) {
                        r = pending;
                        Reference rn = r.next;
                        pending = (rn == r) ? null : rn;
                        r.next = r;
                    } else {
                        try {
                            lock.wait();
                        } catch (InterruptedException x) { }
                        continue;
                    }
                }

                // Fast path for cleaners
                if (r instanceof Cleaner) {
                    ((Cleaner)r).clean();
                    continue;
                }

                ReferenceQueue q = r.queue;
                if (q != ReferenceQueue.NULL) q.enqueue(r);
            }
        }
    }

如果pending为空的时候,会通过lock.wait()一直等在那里,唤醒动作是在jvm里做的,在gc完成后会调用如下方法VM_GC_Operation::doit_epilogue(),在方法末尾会调用lock的notify操作,至于pending队列是什么时候讲引用放进去,其实是在gc的引用处理逻辑中放进去的

void VM_GC_Operation::doit_epilogue() {
  assert(Thread::current()->is_Java_thread(), "just checking");
  // Release the Heap_lock first.
  SharedHeap* sh = SharedHeap::heap();
  if (sh != NULL) sh->_thread_holds_heap_lock_for_gc = false;
  Heap_lock->unlock();
  release_and_notify_pending_list_lock();
}

void VM_GC_Operation::release_and_notify_pending_list_lock() {
instanceRefKlass::release_and_notify_pending_list_lock(&_pending_list_basic_lock);
}

为什么要使用堆外内存以及为什么不能大面积使用堆外内存看原文

猜你喜欢

转载自xiaoxiaoher.iteye.com/blog/2371523