深入分析object.finalize方法的实现原理

author author     2022-08-25     241

关键词:

“物有本末,事有始终。知其先后,则近道矣”

finalize

如果类中重写了finalize方法,当该类对象被回收时,finalize方法有可能会被触发,下面通过一个例子说明finalize方法对垃圾回收有什么影响。

public class FinalizeCase {

    private static Block holder = null;

    public static void main(String[] args) throws Exception {
        holder = new Block();
        holder = null;
        System.gc();
        //System.in.read();
    }

    static class Block {
        byte[] _200M = new byte[200*1024*1024];
    }
}

Block类中声明一个占用内存200M的数组,是为了方便看出来gc之后是否回收了Block对象,执行完的gc日志如下:

技术分享

从gc日志中可以看出来,执行完System.gc()之后,Block对象被如期的回收了,如果在Block类中重写了finalize方法,会是一样的结果么?

static class Block {
    byte[] _200M = new byte[200*1024*1024];
    @Override
    protected void finalize() throws Throwable {
        System.out.println("invoke finalize");
    }
}

  

执行完成gc日志如下:

技术分享

和之前的gc日志进行比较,发现finalize方法确实被触发了,但是Block对象还在内存中,并没有被回收,这是为什么?

下面对finalize方法的实现原理进行分析。

finalize实现原理

JVM源码分析之Java对象的创建过程》一文中分析了Java对象创建的整个过程,代码实现如下:

技术分享

对象的初始化过程会对has_finalizer_flagRegisterFinalizersAtInit进行判断,如果类重写了finalize方法,且方法体不为空,则调用register_finalizer函数,继续看register_finalizer函数的实现:

技术分享

其中Universe::finalizer_register_method()缓存的是jdkjava.lang.ref.Finalizer类的register方法,实现如下:

技术分享

在jvm中通过JavaCalls::call触发register方法,将新建的对象O封装成一个Finalizer对象,并通过add方法添加到Finalizer链表头。

对象OFinalizer类的静态变量unfinalized有联系,在发生GC时,会被判定为活跃对象,因此不会被回收

FinalizerThread线程

Finalizer类的静态代码块中会创建一个FinalizerThread类型的守护线程,但是这个线程的优先级比较低,意味着在cpu吃紧的时候可能会抢占不到资源执行。

技术分享

FinalizerThread线程负责从ReferenceQueue队列中获取Finalizer对象,如果队列中没有元素,则通过wait方法将该线程挂起,等待被唤醒

技术分享

如果返回了Finalizer对象,执行对象的runFinalizer()方法,其实可以发现:在runFinalizer()方法中主动捕获了异常,即使在执行finalize方法抛出异常时,也没有关系。

技术分享

通过hasBeenFinalized方法判断该对象是否还在链表中,并将该Finalizer对象从链表中删除,这样下次gc时就可以把原对象给回收掉了,最后调用了native方法invokeFinalizeMethod,其中invokeFinalizeMethod方法最终会找到并执行对象的finalize方法。

技术分享

ReferenceHandler线程

有个疑问:既然FinalizerThread线程是从ReferenceQueue队列中获取Finalizer对象,那么Finalizer对象是在什么情况下才会被插入到ReferenceQueue队列中?

Finalizer的祖父类Reference中定义了ReferenceHandler线程,实现如下:

技术分享

pending被设置时,会调用ReferenceQueueenqueue方法把Finalizer对象插入到ReferenceQueue队列中,接着通过notifyAll方法唤醒FinalizerThread线程执行后续逻辑,实现如下:

技术分享

pending字段什么时候会被设置?

在GC过程的引用处理阶段,通过oopDesc::atomic_exchange_oop方法把发现的引用列表设置在pending字段所在的地址

技术分享

Finalizer导致的内存泄漏

平常使用的Socket通信,SocksSocketImpl的父类重写了finalize方法

技术分享

这么做主要是为了确保在用户忘记手动关闭socket连接的情况下,在该对象被回收时能够自动关闭socket来释放一些资源,但是在开发过程中,真的忘记手动调用了close方法,那么这些socket对象可能会因为FinalizeThread线程迟迟没有执行到这些对象的finalize方法,而导致一直占用某些资源,造成内存泄露。

 

原文:占小狼

system.gc()与object.finalize()的区别(代码片段)

finalize()是由JVM自动调用的,你可以用System.gc(),但JVM不一定会立刻执行,JVM感觉内存空间有限时,才会开始执行finalize(),至于新的对象创建个数和被收集个数不同是因为收集的对象只和JVM的垃圾收集策略有关。 finalize()和gc()(1... 查看详情

java面试知识点

(1)关于finalize的问题因为不可预测性,以及对垃圾回收性能的影响,Object.finalize()方法不推荐使用,并且在Java9中已经被废弃。Java平台目前在逐步使用java.lang.ref.Cleaner来逐步替代finalize实现。(2)使用setter/getter方法时,注意运... 查看详情

jdk源码分析深入源码分析countdownlatch(代码片段)

前言CountDownLatch是一个闭锁实现,它可以使一个或者多个线程等待一组事件发生。它包含一个计数器,用来表示需要等待的事件数量,coutDown方法用于表示一个事件发生,计数器随之递减,而await方法等待计数器为0之前一直阻塞... 查看详情

面向对象的三大特性

...承Object(不管是java定义的还是自己定义的);;; Object.finalize()-->调用这个方法来释放资源; 多态(执行期间(执行期 查看详情

java并发编程专题系列之深入分析synchronized(基础篇)

synchronized同步关键字简介synchronized是属于JVM层面的一个关键字,底层是通过一个monitor对象(管程对象)来完成,由于wait()/notify()等方法也依赖于monitor对象,所以只有在同步的块或者方法中才能调用wait/notify等方法synchronized同步代码... 查看详情

string类replaceall方法正则替换深入分析

作者网址:https://my.oschina.net/shipley/blog/98973背景:     前几天有人发了一个关于下面问题的贴,对这个有点好奇,故花时间做了点研究。      对单个反斜杠字符串替换成双斜杠的Java实现如... 查看详情

详细深入分析classloader工作机制

申明:本文首发于详细深入分析JavaClassLoader工作机制,如有转载,注明原出处即可,谢谢配合。详细深入分析JavaClassLoader工作机制什么是ClassLoaderClassLoader作用1ClassLoader类结构分析2ClassLoader的等级加载机制Java默认提供的三个ClassLo... 查看详情

深入分析volatile的实现原理

...改的值。它在某些情况下比synchronized的开销更小,本文将深入分析在硬件层面上Inter处理器是如何实现Volatile的,通过深入分析能帮助 查看详情

aspects源码分析(代码片段)

Aspects是一个用来切片编程的开源框架,提供了丰富接口,可以Hook类和单个对象的方法,并提供了原实现前Hook,替换原实现,原实现后Hook等选项。1补充知识点Objective-C里面的方法调用,我们看到的第1个参... 查看详情

aspects源码分析(代码片段)

Aspects是一个用来切片编程的开源框架,提供了丰富接口,可以Hook类和单个对象的方法,并提供了原实现前Hook,替换原实现,原实现后Hook等选项。1补充知识点Objective-C里面的方法调用,我们看到的第1个参... 查看详情

深入分析synchronized的实现原理

深入分析synchronized的实现原理 记得刚刚开始学习Java的时候,一遇到多线程情况就是synchronized,相对于当时的我们来说synchronized是这么的神奇而又强大,那个时候我们赋予它一个名字“同步”,也成为了我们解决多线程情况... 查看详情

java技术专题「原理专题」深入分析java中finalize方法的作用和底层原理

finalize方法是什么finalize方法是Object的protected方法,Object的子类们可以覆盖该方法以实现资源清理工作,GC在首次回收对象之前调用该方法。finalize方法与C++的析构函数的区别finalize方法与C++中的析构函数不是对应的,C++中的析构... 查看详情

caffe深入分析(源码)

Caffe的整体流程图:程序入口:main()1intmain(intargc,char**argv){2.....3returnGetBrewFunction(caffe::string(argv[1]))();4....5} g_brew_map实现过程,首先通过typedef定义函数指针typedefint(*BrewFunction)();这个是用typedef定义函数指针方法。 查看详情

jvm技术专题「原理专题」深入分析java中finalize方法的作用和底层原理(代码片段)

finalize方法是什么finalize方法是Object的protected方法,Object的子类们可以覆盖该方法以实现资源清理工作,GC在首次回收对象之前调用该方法。finalize方法与C++的析构函数的区别finalize方法与C++中的析构函数不是对... 查看详情

深入分析zookeeper的实现原理

zookeeper的由来  分布式系统的很多难题,都是由于缺少协调机制造成的。在分布式协调这块做得比较好的,有Google的Chubby以及Apache的Zookeeper。GoogleChubby是一个分布式锁服务,通过Google Chubby来解决分布式协作、Master选举等与... 查看详情

深入理解jvm原理之逃逸分析

...下jvm中的分析技术优化---逃逸分析;内容大部分源自于《深入理解Java虚拟机》;逃逸分析一般分为两种:一种基本行为就是分析对象的动态作用域,当一个对象在方法中定义后,它可能被外部的方法所引用,例如作为调用参数... 查看详情

深入剖析threadlocal

Java并发编程:深入剖析ThreadLocal  想必很多朋友对ThreadLocal并不陌生,今天我们就来一起探讨下ThreadLocal的使用方法和实现原理。首先,本文先谈一下对ThreadLocal的理解,然后根据ThreadLocal类的源码分析了其实现原理和使用需要... 查看详情

深入分析classloader工作机制

ClassLoader较为深入分析。from<深入分析JavaWeb>加载CLASS到JVM中,审查每个类应该由谁加载,父优先的等级加载机制。加载机制ClassLoader类结构分析ClassLoader抽象类,有很多子类,一般在实现自己的ClassLoader时候,一般都会继承URLC... 查看详情