HotSpot SA #3:FinalizerInfo

前面我们已经把玩过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#[email protected],所以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,今天就先到这吧^_^

参考资料

时间: 2024-11-08 06:11:50

HotSpot SA #3:FinalizerInfo的相关文章

HotSpot SA(三):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.

转:什么是即时编译(JIT)!?OpenJDK HotSpot VM剖析

重点 应用程序可以选择一个适当的即时编译器来进行接近机器级的性能优化. 分层编译由五层编译构成. 分层编译提供了极好的启动性能,并指导编译的下一层编译器提供高性能优化. 提供即时编译相关诊断信息的JVM开关. 像内联化和向量化之类的优化进一步增强了性能. OpenJDK HotSpot Java Virtual Machine被人亲切地称为Java虚拟机或JVM,由两个主要组件构成:执行引擎和运行时.JVM和Java API组成Java运行环境,也称为JRE. 在本文中,我们将探讨执行引擎,特别

Linux运维六:用户管理及用户权限设置

Linux 系统是一个多用户多任务的分时操作系统,任何一个要使用系统资源的用户,都必须首先向系统管理员申请一个账号,然后以这个账号的身份进入系统.用户的账号一方面可以帮助系统管理员对使用系统的用户进行跟踪,并控制他们对系统资源的访问:另一方面也可以帮助用户组织文件,并为用户提供安全性保护.每个用户账号都拥有一个惟一的用户名和各自的口令.用户在登录时键入正确的用户名和口令后,就能够进入系统和自己的主目录. 实现用户账号的管理,要完成的工作主要有如下几个方面: · 用户账号的添加.删除与修改. ·

JVM自动内存管理:对象判定和回收算法

可回收对象的判断方法 1.引用计数算法 2.可达性分析算法 引用计数算法 给对象中添加一个引用计数器,每当有一个地方引用它时,计数器值就加1:当引用失效时,计数器值就减1:任何时刻计数器为0的对象就是不可能再被使用的. 引用计数算法的缺陷:循环引用 可达性分析算法 可达性分析算法基本原理: 通过一些列的称为"GC Roots"的对象作为起始点,从这些节点开始进行向下搜索,搜索 所走过的路径成为引用链(Reference Chain),当一个对象到GC Roots没有任何引用连(用图论的

[Sdoi2016]生成魔咒[SAM or SA]

4516: [Sdoi2016]生成魔咒 Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 1017  Solved: 569[Submit][Status][Discuss] Description 魔咒串由许多魔咒字符组成,魔咒字符可以用数字表示.例如可以将魔咒字符 1.2 拼凑起来形成一个魔咒串 [1,2]. 一个魔咒串 S 的非空字串被称为魔咒串 S 的生成魔咒. 例如 S=[1,2,1] 时,它的生成魔咒有 [1].[2].[1,2].[2

java.lang.OutOfMemoryError:GC overhead limit exceeded填坑心得

我遇到这样的问题,本地部署时抛出异常java.lang.OutOfMemoryError:GC overhead limit exceeded导致服务起不来,查看日志发现加载了太多资源到内存,本地的性能也不好,gc时间消耗的较多.解决这种问题两种方法是,增加参数,-XX:-UseGCOverheadLimit,关闭这个特性,同时增加heap大小,-Xmx1024m.坑填了,but why? OOM大家都知道,就是JVM内存溢出了,那GC overhead limit exceed呢? GC ov

SQL error: cannot use the special principal 'sa'

?? SQL error: cannot use the special principal 'sa' 解决方案: use dbname EXEC sp_changedbowner 'dbname' 参考:https://msdn.microsoft.com/en-us/library/ms178630.aspx 版权声明:本文为博主原创文章,未经博主允许不得转载. SQL error: cannot use the special principal 'sa'

第五篇 Integration Services:增量加载-Deleting Rows

本篇文章是Integration Services系列的第五篇,详细内容请参考原文. 在上一篇你学习了如何将更新从源传送到目标.你同样学习了使用基于集合的更新优化这项功能.回顾增量加载记住,在SSIS增量加载有三个使用案例:1.New rows-add rows to the destination that have been added to the source since the previous load.2.Updated rows-update rows in the destin

sqlserver 2008 sa登陆的一些问题

sqlserver 2008 sa登陆 无法连接到(local)? 遇到这个问题请确保SQL主服务是开启状态: ok接下来把服务器名换成 计算机名\实例名  再次使用sa登陆,如下: 是不是可以了呢? 如果你想使用"(local)","."进行登陆,可以到SQL Server配置管理器进行设置: 右键点击“别名”--> 新建别名 协议里选择“Named Pipes”,然后别名里输入“(local)”,服务器那里输入“机器名\服务器名”,如:“OIAR7RFIC