码迷,mamicode.com
首页 > 其他好文 > 详细

HotSpot SA #3:FinalizerInfo

时间:2015-03-10 21:36:43      阅读:164      评论:0      收藏:0      [点我收藏+]

标签:sa   finalize   gc   

前面我们已经把玩过SA工具中的JStackClassDump,今天再来看个好玩的,FinalizerInfo

Object#finalize

看名字多半能猜到,FinalizerInfo是跟Object的finalize方法的执行有关的,

Called by the garbage collector on an object when garbage collection determines that there are no more references to the object.

然后我翻了好久的官方文档才找到这篇Troubleshooting Guide for HotSpot VM中的这一小节有对finalize相关实现的描述 ,

One other potential source of OutOfMemoryError arises with applications that make excessive use of finalizers. If a class has a finalize method, then objects of that type do not have their space reclaimed at garbage collection time. Instead, after garbage collection the objects are queued for finalization, which occurs at a later time. In the Sun implementation, finalizers are executed by a daemon thread that services the finalization queue. If the finalizer thread cannot keep up with the finalization queue, then the Java heap could fill up and OutOfMemoryError would be thrown.

也就是说,在对象被回收之前,需要执行finalize方法,而finalize方法的执行又是需要排着队由某个线程来一个个消费的。下面我们通过会阻塞住的finalize方法来验证看看,

    private static class Foo {
        @Override
        protected void finalize() throws Throwable {
            System.out.println("finalize#" + this);
            super.finalize();
            System.in.read(); // 这个finalize方法将会卡住
        }
    }

    private static class Bar {
        @Override
        protected void finalize() throws Throwable {
            System.out.println("finalize#" + this);
            super.finalize();
            System.in.read(); // 这个finalize方法也会卡住
        }
    }

    public static void main(String[] args) throws Exception{

        foo();
        bar();

        System.gc();
        Thread.sleep(2000);
        System.in.read();
    }

    private static Foo foo() {
        return new Foo();
    }

    private static Bar bar() {
        return new Bar();
    }

如果上述没错,那么Foo跟Bar只要其中一个的finalize方法执行了,另一个必定得不到执行,因为单个队列,有一个卡住了那么其后续的必然也无法被消费了。
事实确实如此,输出只有一行finalize#me.kisimple.just4fun.Main$Bar@571688,所以Foo必定是在等待着被finalize。这时候FinalizerInfo就派上用场了,用它我们可以观察VM中有哪些正在等待被finalize的对象,

# java7 sun.jvm.hotspot.tools.FinalizerInfo 5960
Attaching to process ID 5960, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 24.65-b04
Number of objects pending for finalization: 33

Count   Class description
-------------------------------------------------------
17  java.util.zip.ZipFile$ZipFileInputStream
11  java.util.zip.ZipFile$ZipFileInflaterInputStream
4   java.io.FileInputStream
1   me.kisimple.just4fun.Main$Foo

妥妥的我们看到了1 me.kisimple.just4fun.Main$Foo,验证了文档中的描述。

FinalizerThread

更进一步,我们可以冲进FinalizerInfo的源码看看,

        /*
         * The implementation here has a dependency on the implementation of
         * java.lang.ref.Finalizer. If the Finalizer implementation changes it‘s
         * possible this method will require changes too. We looked into using
         * ObjectReader to deserialize the objects from the target VM but as
         * there aren‘t any public methods to traverse the queue it means using
         * reflection which will also tie us to the implementation.
         *
         * The assumption here is that Finalizer.queue is the ReferenceQueue
         * with the objects awaiting finalization. The ReferenceQueue queueLength
         * is the number of objects in the queue, and ‘head‘ is the head of the
         * queue.
         */

注释就已经告诉我们,存放等待finalize的对象的队列就是在java.lang.ref.Finalizer.queue。然后去看看Finalizer的源码,可以看到消费这个queue的线程,也就是Finalizer.FinalizerThread线程,

        public void run() {
            if (running)
                return;

            // Finalizer thread starts before System.initializeSystemClass
            // is called.  Wait until JavaLangAccess is available
            while (!VM.isBooted()) {
                // delay until VM completes initialization
                try {
                    VM.awaitBooted();
                } catch (InterruptedException x) {
                    // ignore and continue
                }
            }
            final JavaLangAccess jla = SharedSecrets.getJavaLangAccess();
            running = true;
            for (;;) {
                try {
                    Finalizer f = (Finalizer)queue.remove();
                    f.runFinalizer(jla);
                } catch (InterruptedException x) {
                    // ignore and continue
                }
            }
        }
    private void runFinalizer(JavaLangAccess jla) {
        synchronized (this) {
            if (hasBeenFinalized()) return;
            remove();
        }
        try {
            Object finalizee = this.get();
            if (finalizee != null && !(finalizee instanceof java.lang.Enum)) {

                ///////////////////////////////////////////////
                // 最后由JavaLangAccess来真正执行Object#finalize了
                jla.invokeFinalize(finalizee);

                /* Clear stack slot containing this variable, to decrease
                   the chances of false retention with a conservative GC */
                finalizee = null;
            }
        } catch (Throwable x) { }
        super.clear();
    }

还有另一个方法,我们可以在FinalizerThread打断点进行调试,这样也是能验证我们的想法的。alright,今天就先到这吧^_^

参考资料

HotSpot SA #3:FinalizerInfo

标签:sa   finalize   gc   

原文地址:http://blog.csdn.net/kisimple/article/details/43784217

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!