首页 > 编程语言 > 详细

Java中的DirectByteBuffer是如何被回收的

时间:2020-04-26 21:07:32      阅读:113      评论:0      收藏:0      [点我收藏+]

在SunJDK中,java.nio.DirectByteBuffer是由ByteBuffer#allocateDirect(int)创建的,它有一个类型为sun.misc.Cleaner的字段,Cleaner继承了java.lang.ref.PhantomReference(虚引用).
DirectByteBuffer创建时cleaner字段赋值,传入一个Runnable类型的Deallocator对象(用于释放内存)

cleaner = Cleaner.create(this, new Deallocator(base, size, cap));

Cleaner被收集并且即将移入关联的引用队列时,GC收集相关的线程通过ReferenceHandlerCleaner类型的Reference实例特殊处理:将引用实例向下转型为Cleaner,然后调用Cleaner#clean(),最终会回到调用DirectByteBuffer$Deallocator#run(),进而调用Unsafe#freeMemory(long).

可以看到,并没有对Object#finalize()的调用.

总之,如果释放对DirectByteBuffer实例的引用,一般就不会耗尽内存,只要垃圾收集器有机会注意到这种情况,之后它的引用处理线程就会执行上述过程.

  • Cleaner对象
    虚引用的子类型.对象构造时传入一个Runnable用于在引用处理线程执行时调用.

  • Reference.java 注意本代码的第55行

    private static class ReferenceHandler extends Thread {

        private static void ensureClassInitialized(Class<?> clazz) {
            try {
                Class.forName(clazz.getName(), true, clazz.getClassLoader());
            } catch (ClassNotFoundException e) {
                throw (Error) new NoClassDefFoundError(e.getMessage()).initCause(e);
            }
        }

        static {
            // pre-load and initialize InterruptedException and Cleaner classes
            // so that we don‘t get into trouble later in the run loop if there‘s
            // memory shortage while loading/initializing them lazily.
            ensureClassInitialized(InterruptedException.class);
            ensureClassInitialized(Cleaner.class);
        }

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

        public void run() {
            while (true) {
                tryHandlePending(true);
            }
        }
    }

    /**
     * Try handle pending {@link Reference} if there is one.<p>
     * Return {@code true} as a hint that there might be another
     * {@link Reference} pending or {@code false} when there are no more pending
     * {@link Reference}s at the moment and the program can do some other
     * useful work instead of looping.
     *
     * @param waitForNotify if {@code true} and there was no pending
     * {@link Reference}, wait until notified from VM
     * or interrupted; if {@code false}, return immediately
     * when there is no pending {@link Reference}.
     * @return {@code true} if there was a {@link Reference} pending and it
     * was processed, or we waited for notification and either got it
     * or thread was interrupted before being notified;
     * {@code false} otherwise.
     */
    static boolean tryHandlePending(boolean waitForNotify) {
        Reference<Object> r;
        Cleaner c;
        try {
            synchronized (lock) {
                if (pending != null) {
                    r = pending;
                    // ‘instanceof‘ might throw OutOfMemoryError sometimes
                    // so do this before un-linking ‘r‘ from the ‘pending‘ chain...
                    c = r instanceof Cleaner ? (Cleaner) r : null;
                    // unlink ‘r‘ from ‘pending‘ chain
                    pending = r.discovered;
                    r.discovered = null;
                } else {
                    // The waiting on the lock may cause an OutOfMemoryError
                    // because it may try to allocate exception objects.
                    if (waitForNotify) {
                        lock.wait();
                    }
                    // retry if waited
                    return waitForNotify;
                }
            }
        } catch (OutOfMemoryError x) {
            // Give other threads CPU time so they hopefully drop some live references
            // and GC reclaims some space.
            // Also prevent CPU intensive spinning in case ‘r instanceof Cleaner‘ above
            // persistently throws OOME for some time...
            Thread.yield();
            // retry
            return true;
        } catch (InterruptedException x) {
            // retry
            return true;
        }

        // Fast path for cleaners
        if (c != null) {
            c.clean();
            return true;
        }

        ReferenceQueue<? super Object> q = r.queue;
        if (q != ReferenceQueue.NULL) q.enqueue(r);
        return true;
    }

Java中的DirectByteBuffer是如何被回收的

原文:https://www.cnblogs.com/onion94/p/Java-DirectByteBuffer-How-Release.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!