【java.lang.ref】当WeakReference的referent重写了finalize方法时会发生什么

2023-11-15

问题

question:当WeakReference的referent重写了finalize方法时会发生什么?

 


测试代码

JVM中是存在这样的情况的:一个Java对象,重写了finalize方法,在使用的过程中又被SoftReference或WeakReference或PhantomReference封装,这时候JVM是怎么处理该referent的?

软引用受LRU策略的影响,不太好探究。直接使用虚引用在JVM中的处理流程和弱引用一致,但还需要提供一个关系不大的ReferenceQueue,所以选择弱引用来探究这个问题,是更合适的。

用代码来描述这个问题:

public class ThisEscapeAndWeakRef {
    private static ThisEscapeAndWeakRef escape = null;

    @Override
    // finalize逃逸
    protected void finalize() throws Throwable {
    	ThisEscapeAndWeakRef.escape = this;
    	System.out.println("finalize method running");
    }

    // 用于测试finalize逃逸是否成功,如果成功escape != null,则可以调用该方法
    // 否则报空指针异常
    public void isAlive(String step) {
	    System.out.println(step + ": " + this + " is alive!");
    }

    public static void main(String[] args) {
        // A
	    escape = new ThisEscapeAndWeakRef();
        // B
	    WeakReference<ThisEscapeAndWeakRef> weakReference = new WeakReference<ThisEscapeAndWeakRef>(escape);
        // C
	    escape = null;
	    System.gc();
        // D
	    if(escape != null)
	        escape.isAlive("D");
        // E
	    System.out.println("E: " + weakReference.get());
        // F
	    try {
	        Thread.sleep(1000);
    	} catch (InterruptedException e) {
    	    // TODO Auto-generated catch block
    	    e.printStackTrace();
    	}
        // G
    	if(escape != null)
	        escape.isAlive("G");
    }
}

输出结果:

E: null
finalize method running
G: cn.wxy.gc.ThisEscapeAndWeakRef@5b6f7412 is alive!

 


分析

我们按照发生时间的先后顺序来分析:

虚拟机启动阶段

  • 虚拟机默认参数启动:-XX:+RegisterFinalizersAtInit,表示创建对象在调用构造函数返回时,会将重写了java.lang.Object.fianlzie()方法的对象注册为Finalizer

类加载阶段

  • 在虚拟机将ThisEscapeAndWeakRef类在磁盘上对应的class文件加载到持久代或metaspace时,会解析class文件中的方法,发现重写了java.lang.Object.finalize()方法,于是置1该类对应的访问标识位JVM_ACC_HAS_FINALIZER
  • 在ThisEscapeAndWeakRef类加载的链接阶段,因为参数RegisterFinalizersAtInit=true并且访问标志JVM_ACC_HAS_FINALIZER置1,所以会将该class文件中原来编译的Object的构造函数中的return指令重写为_return_register_finalizer指令

执行main方法

对应示例代码注释中的每个step:

  • A:实例化ThisEscapeAndWeakRef escape,因为在类链接阶段重写了构造函数的return指令,所以通过invokespecial指令调用构造函数返回时重写指令_return_register_finalizer会将escape封装成一个Finalizer
  • B:将escape封装成一个WeakReference,escape作为弱引用的referent

现状:此时在JVM中,即存在Finalizer也存在WeakReference,escape对象即是Finalizer的referent,也是WeakReference的referent

  • C:escape = null,断开escape的强引用并发起一次显式GC,此时Finalizer和WeakReference都会被GC处理,但是处理顺序无法预测,取决于GC做可达性分析时先扫到谁

流程:

  • 对于Finalizer:被GC挂到pending-reference list,随后reference-handler线程取走并放入关联的ReferenceQueue,然后FinalizerThread从ReferenceQueue拿到Finalizer对象,调用Finalizer.get()方法拿到escape,最后调用escape.finalize()并clear referent
  • 对于WeakReference:被GC clear referent并挂到pending-reference list,随后reference-handler线程取走,因为没有关联队列,所以流程结束
  • D:如果escape逃逸成功,则escape != null,代码执行会调用isAlive方法打印,测试结果该句代码没有执行,所以finalize逃逸还没有成功
  • E:通过weakReference.get()拿escape,返回值为null,说明weakReference已经被GC处理,已经被挂到pending-reference list上
  • F:因为FinalizerThread不是最高优先级的进程,所以sleep一秒,在此过程中,发现escape.finalize方法在FinalizerThread线程中被调用执行
  • G:在F处sleep代码执行的过程中发现escape.finalize方法已经被FinalizerThread调用,再次执行和D处一样的代码,escape.isAlive被调用执行,finalize逃逸成功

 


结论

一个Java对象,重写了finalize方法,在使用的过程中又被SoftReference/WeakReference/PhantomReference封装:

  • 此时在JVM中既有封装了该对象的Finalizer,又有相关的SoftReference/WeakReference/PhantomReference
  • 当referent失去强引用时,Finalizer和SoftReference/WeakReference/PhantomReference被GC处理的顺序是不确定的
  • A:假设先处理SoftReference/WeakReference/PhantomReference:
    • 此时GC会先clear referent,然后再继续Java引用流程
    • 等GC开始处理Finalizer时,有且只有Finalizer还持有referent的引用,在调用referent.finalize方法后无论后续是否发生finalize逃逸,referent都已经不再被任何Java引用封装
  • B:假设先处理Finalizer:
    • Finalizer被GC挂到pending-reference list上时不会clear referent,而稍后FinalizerThread线程进行处理时可能会发生finalize逃逸
    • 如果GC处理SoftReference/WeakReference/PhantomReference在finalize逃逸之前,则如同A假设
    • 如果GC处理SoftReference/WeakReference/PhantomReference在finalize逃逸之后,此时referent重新变成强可达,所以SoftReference/WeakReference/PhantomReference不会被GC处理,而在Finalizer的流程中已经clear referent,所以此时只有SoftReference/WeakReference/PhantomReference仍旧持有referent的引用

 


系列目录

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

【java.lang.ref】当WeakReference的referent重写了finalize方法时会发生什么 的相关文章

  • 【java.lang.ref】当WeakReference的referent重写了finalize方法时会发生什么

    问题 question 当WeakReference的referent重写了finalize方法时会发生什么 测试代码 JVM中是存在这样的情况的 一个Java对象 重写了finalize方法 在使用的过程中又被SoftReference或
  • 什么时候 dispose 方法不会被调用?

    我正在读书this前几天的文章 我想知道为什么有 Finalizer 和 Dispose 方法 我读here关于为什么您可能想要将 Dispose 添加到 Finalizer 中 我好奇的是 什么时候 Finalizer 会通过 Dispo
  • (.net) CriticalFinalizerObject - 它到底有什么作用?

    我对这个类的理解是 当您想确保调用该类的终结器 析构函数 时应该使用它 但从我所做的一些测试来看 这似乎并不正确 如果它不能确保调用 dispose 方法 是否还有其他方法可以做到这一点 例如 如果我想确保运行某些代码来结束我的对象 即使我
  • 如何在 Fortran 中正确地终结对象?

    我有一个解析文本文件的对象 这是我的主要程序 program main use Parser class implicit none type Parser Parser call Parser ProcessFile data txt c
  • 终结器和处置

    我有一个名为BackgroundWorker有一个线程不断运行 要关闭该线程 可以使用名为的实例变量stop需要是true 为了确保在类使用完毕后释放线程 我添加了IDisposable和一个调用的终结器Dispose 假如说stop tr
  • Java 8 中强可达对象上的 Finalize() 调用

    我们最近将消息处理应用程序从 Java 7 升级到 Java 8 自升级以来 我们偶尔会遇到一个异常 即流在读取时已关闭 日志显示终结器线程正在调用finalize 在保存流的对象上 进而关闭流 代码的基本轮廓如下 MIMEWriter w
  • 使用终结器清理弱引用缓存?

    假设我有一个由弱引用或软引用组成的缓存 这些弱 软引用需要在某个时候关闭 理想情况下 一旦 GC 从缓存中删除对象 就应该关闭这些对象 使用终结器 清理器来关闭这些资源 同时仍然在程序末尾循环缓存并手动关闭它们是否合适 public voi
  • F# 析构函数的等效项

    我正在将一个将非托管库包装的 C 类转换为 F 我遇到了重写随后的析构函数的看似简单的问题 class Wrapper P Invoke ellided private SomeType x public Wrapper x new Som
  • 我们可以关闭终结器吗?

    由于无法保证终结器何时运行以及是否会运行 而且如今终结器几乎被认为是一种气味 有什么方法可以说服 JVM 完全跳过所有终结过程吗 我问这个问题是因为我们有一个庞大的应用程序 当它迁移到较新的 JVM 现阶段不确定是哪个 时 它会因为看起来非
  • 如何正确实现 Java 中检测资源泄漏的终结器

    假设我已经创建了一些带有 close 方法的资源类来清理资源 并且如果有人忘记调用 close 我想重写 Finalize 来释放资源 并打印警告 如何正确地做到这一点 是否仅建议用于本机 JNI 分配 资源 如果您使用终结器对另一个已终结
  • 包含非托管对象的 ConcurrentBag 的完成

    我在正确处理 Dispose Finalization 时遇到问题ConcurrentBag包含非托管对象 运行下面的代码 通常 会产生一个ObjectDisposedException Cannot access a disposed o
  • 从 Finalizer 调用托管资源是否安全? (如果我检查空)

    打电话不安全吗 组件 Dispose 如果我检查空 如果我将代码更改为 MyResource Dispose public void Dispose Check to see if Dispose has already been call
  • 为什么要在对象上调用终结器

    下面的示例程序表现出令人惊讶的最终行为 class Something public void DoSomething Console WriteLine Doing something Something Console WriteLin
  • 如何找到终结器队列挂起的原因?

    我有一个应用程序从 go 开始就经历了缓慢的内存泄漏 Using ANTS 内存分析器我可以看到所有泄漏的内存都由终结器队列的 GC 根保存 我怀疑可能发生的情况是终结器死锁 等待锁变得可用 我们的类都没有实现显式终结器 我们通常避免使用它
  • 测试终结器和 IDisposable

    问题是如何测试调用 Finalize 时对象释放资源的事实 该类的代码 public class TestClass IDisposable public bool HasBeenDisposed get private set publi
  • 为什么 Java 终结器存在安全问题?

    我正在阅读有效的 Java 作者 Joshua Bloch https www amazon com au Effective Java Joshua Bloch dp 0134685997 In 第 8 项 避免定型剂和清洁剂 of 第2
  • 关闭连接是最终确定最佳实践吗? [复制]

    这个问题在这里已经有答案了 可能的重复 为什么要实现finalize https stackoverflow com questions 158174 why would you ever implement finalize 我看到一些j
  • 如何识别GC Finalizer线程?

    我有一个 NET C 多线程应用程序 我想知道某个方法是否在 Finalizer 线程内运行 我尝试过使用 Thread CurrentThread Name 但它不起作用 返回 null 有人知道如何查询当前线程以发现它是否是 Final
  • 终结器线程的范围是什么 - 每个应用程序域或每个进程?

    根据我的所有阅读 应该有一个 GC 线程来调用所有终结器 现在的问题是这个 一个 线程的范围是什么 每个进程或每个应用程序域 因为域的整体目的是在一个进程空间中分离并创建 独立 的不同应用程序 I read here http dn cod
  • 使用带有终结器的 C++/CLI 定义类时 C# 中的内存泄漏

    当我在 C CLI DLL 中实现一个类时 public ref class DummyClass protected DummyClass some dummy code std cout lt lt hello lt lt std en

随机推荐

  • C++ *和&

    简单理解 是指向内存的地址变量 是取变量的地址 介绍参见 https www cnblogs com mr stn p 9037773 html简介
  • spring boot项目显示3行日志错误,内置tomcat不可使用

    spring boot项目显示3行日志错误 内置tomcat不可使用 首先这中错误是只显示三行 第一种方法是没有用spring boot starter web 在pom中将这个依赖放在第一个 第二种方法是继承ServletInitiali
  • 详解如何将python3.6软件的py文件打包成exe程序

    本文来源于公众号 csdn2299 喜欢可以关注公众号 程序员学府 这篇文章主要介绍了详解如何将python3 6软件的py文件打包成exe程序 小编觉得挺不错的 现在分享给大家 也给大家做个参考 一起跟随小编过来看看吧 在我们完成一个Py
  • Codeforces Round 867 (Div. 3)(A题到E题)

    链接 Dashboard Codeforces Round 867 Div 3 Codeforces 头一次div3做出来四题 第五题也差临门一脚 赛后看到别人e题跟自己几乎一样的思路肠悔青了 还得练才行 A TubeTube Feed 签
  • 网络系列--如何理解网络“软化”?简要整理分析网络软化的赋能技术。

    文章目录 对网络软化的理解 SDN NFV NFV与SDN简单对比 如何理解网络 软化 简要整理分析网络软化的赋能技术 对网络软化的理解 在互联网飞速发展的今天 通信网络规模爆发式的增长以及客户对网络性能需求的多样性 对网络的灵活性和敏捷性
  • 【0day】复现时空智友企业流程化管控系统SQL注入漏洞

    注 该文章来自作者日常学习笔记 请勿利用文章内的相关技术从事非法测试 如因此产生的一切不良后果与作者无关 目录 一 漏洞描述 二 影响版本 三 资产测绘 四 漏洞复现 一 漏洞描
  • cocos2d-x实例学习(8)之CCJumpTo和CCJumpBy

    CCJumpTo和CCJumpBy概念 CCJumpTo 把某一CCSprite跳到某一位置 CCJumpBy 把某一CCSprite跳起一段距离 它有一个方法reverse 它让对象按原路径返回 CCJumpTo和CCJumpBy示例 创
  • react中context的使用详解

    先说一说context是干什么的 跨层级通信 一般组件通信的方法都是通过props来传递 不过这种方法当有了第三级或往后孙孙组件的时候就显得比较繁琐 以及不好修改 这时我们可以使用context来管理这些数据 不管多少层级都能让想要使用这些
  • 线程函数不能为类成员函数_GPU编程2CUDA核函数和线程配置

    CUDA核函数 在GPU上执行的函数称为CUDA核函数 Kernel Function 核函数会被GPU上多个线程执行 我们可以在核函数中获取当前线程的ID CUDA核函数的定义 global void addKernel int c co
  • 堆栈桢的生成原理

    摘要 那么Windbg分析Dump时 会如何推理堆栈过程呢 如果每个函数都是有标准的push ebp 那么按照ebp递推就可以了 否这就只能用其他方法分析 比如看看堆栈里某个地址是不是函数返回地址 该地址属于某个模块的代码段 这样就可以确定
  • mac系统下面调用brew报错core_ext/kernel_require.rb:55:in `require': cannot load such file

    mac系统下面调用brew报错 如下所示 usr local Homebrew Library Homebrew vendor portable ruby 2 3 7 lib ruby 2 3 0 rubygems core ext ker
  • [598]Windows版InfluxDB及相关软件安装与配置

    公司在做一个工业监控系统 虽然数据采集点并不算多但是数据量积累下来也非常大 使用mysql数据库进行数据存储和查询时很慢 所以让我调研一下时序数据库 通过调研和了解时序数据库在海量数据的读取和写出都比关系型数据库和NoSql快很多 有人做过
  • 如何向 Pandas DataFrame 添加行

    您可以使用df loc 函数在Pandas DataFrame的末尾添加一行 add row to end of DataFrame df loc len df index value1 value2 value3 您可以使用df appe
  • 计算机网络基本概念相关习题(二)

    一 单项选择题 1 不是对网络模型进行分层的目标 A 提供标准语言 B 定义功能执行的方法 C 定义标准界面 D 增加功能之间的独立性 2 将用户数据分成一个个数据块传输的优点不包括 A 减少延迟时间 B 提高错误控制效率 C 使多个应用更
  • 毕业答辩模板

    毕业答辩准备工作 一 首先是开场白 各位老师 上午好 我叫 是 级 班的学生 我的论文题目是 论文是在 导师的悉心指点下完成的 在这里我向我的导师表示深深的谢意 向各位老师不辞辛苦参加我的论文答辩表示衷心的感谢 并对三年来我有机会聆听教诲的
  • 三层架构、MVC、前后分离的一些知识

    三层架构 MVC 前后分离的一些知识 三层架构模型 MVC模式 三层架构与 MVC 架构区别 前后端分离开发时的变化 一个前后端分离项目的分层 前端 MVVM 后端 Service层 Model层 Mapper映射 BLL业务逻辑层 DAL
  • FreeRTOS笔记(十)中断

    中断 当CPU在执行某一事件A时 发生另外一个更重要紧急的事件B请求CPU去处理 产生了中断 于是CPU暂时中断当前正在执行的事件A任务而对对事件B进行处理 CPU处理完事件B后再返回之前中断的位置继续执行原来的事件A 这一过程统称为中断
  • 第四章 数据的预处理与特征构建(续)

    申请评分卡模型 数据的预处理与特征构建 续 课程简介 逻辑回归模型的特征需要是数值型 因此类别型变量不能直接放入模型中去 需要对其进行编码 此外 为了获取评分模型的稳定性 建模时需要对数值型特征做分箱的处理 最终在带入模型之前 我们还需要对
  • git 拉取分支到本地文件夹!

    前言 我现在需要改项目 我把项目来下来了却发现只需要修改某个分支的项目 所以只需要拉下项目的某个分支就行了 废话不多说直接开始教程 目录 1 创建本地仓库 2 与远程仓库建立联系 3 确定你需要拉的分支名 4 本地创建的分支与远程分支相互连
  • 【java.lang.ref】当WeakReference的referent重写了finalize方法时会发生什么

    问题 question 当WeakReference的referent重写了finalize方法时会发生什么 测试代码 JVM中是存在这样的情况的 一个Java对象 重写了finalize方法 在使用的过程中又被SoftReference或