HotSpot SA #3:FinalizerInfo,

來源:互聯網
上載者:User

HotSpot SA #3:FinalizerInfo,

前面我們已經把玩過SA工具中的JStack和ClassDump,今天再來看個好玩的,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 5960Attaching to process ID 5960, please wait...Debugger attached successfully.Server compiler detected.JVM version is 24.65-b04Number of objects pending for finalization: 33Count   Class description-------------------------------------------------------17  java.util.zip.ZipFile$ZipFileInputStream11  java.util.zip.ZipFile$ZipFileInflaterInputStream4   java.io.FileInputStream1   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,今天就先到這吧^_^

參考資料
  • http://openjdk.java.net/jeps/132
  • http://docs.oracle.com/javase/7/docs/webnotes/tsg/TSG-VM/html/memleaks.html#gbyvh

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.