java中finalized的用法_java中的引用类型

java中finalized的用法_java中的引用类型我们通常用构造器来创建对象,而Finalize正好相反,构造方法执行对象的初始化操作,finalize方法执行对象的销毁操作.那我们什么时候需要使用finalize方法呢,我们都知道Java里垃圾回收器可以回收对象使用的内存空间,但是对象可能会持有很多资源比如Socket、文件句柄等,垃圾收集器无法回收这些资源,因此你需要使用finalize方法帮助GC回收这些资源,比如关闭打开的文件或者网元资源…

大家好,又见面了,我是你们的朋友全栈君。如果您正在找激活码,请点击查看最新教程,关注关注公众号 “全栈程序员社区” 获取激活教程,可能之前旧版本教程已经失效.最新Idea2022.1教程亲测有效,一键激活。

Jetbrains全系列IDE使用 1年只要46元 售后保障 童叟无欺

我们通常用构造器来创建对象,而Finalize正好相反,构造方法执行对象的初始化操作,finalize方法执行对象的销毁操作.

那我们什么时候需要使用finalize方法呢,我们都知道Java里垃圾回收器可以回收对象使用的内存空间,但是对象可能会

持有很多资源比如Socket、文件句柄等,垃圾收集器无法回收这些资源,因此你需要使用finalize方法帮助GC回收这些

资源,比如关闭打开的文件或者网元资源,删除临时文件等.

一个例子

Object类是所有类的父类,如果你去查看java.lang.Object类的源码,你会发现里面有个finalize方法,这个方法没有默认实现,需要子类根据实际情况重写这个方法,但是如果不恰当使用finalize方法可能会造成很大的负面影响,比如下面的例子:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16public class Finalizer{

@Override

protected void finalize() throws Throwable{

while (true) {

Thread.yield();

}

}

public static void main(String str[]){

while (true) {

for (int i = 0; i < 100000; i++) {

Finalizer force = new Finalizer();

}

}

}

}

当我们运行上述代码时,可以看到创建大量的Finalizer对象,运行一段时间后一般出现以下两种结果:

JVM异常退出并且生成了内存镜像Dump

JVM抛出了一个异常:Out of Memory:GC OverHead limit exceeded.

不管上述两种情况,JVM都崩溃了,那到底执行finalize方法时发生了什么.Jvm会给每个实现了finalize方法的实例创建一个监听,这个称为Finalizer,每次调用对象的finalize方法时,JVM会创建一个java.lang.ref.Finalizer对象,这个Finalizer对象会持有这个对象的引用,由于这些对象被Finilizer对象引用了,当对象数量较多时,就会导致Eden区空间满了,经历多次youngGC后可能对象就进入到老年代了.

java.lang.ref.Finalizer类继承自java.lang.ref.FinalReference,也是Refence的一种,因此Finalizer类里也有一个引用队列,这个引用队列是JVM和垃圾回收器打交道的唯一途径,当垃圾回收器需要回收该对象时,会把该对象放到引用队列中,这样java.lang.ref.Finalizer类就可以从队列中取出该对象,执行对象的finalize方法,并清除和该对象的引用关系.需要注意的是只有finalize方法实现不为空时JVM才会执行上述操作,JVM在类的加载过程中会标记该类是否为finalize类.

GC怎么处理这些对象呢

当老年代空间达到了OldGC条件时,JVM执行一次OldGC,当OldGC执行后JVM检测到这些对象只被Finalizer对象引用,这些对象会被标记成要被清除的对象,GC会把所有的Finalizer对象放入到一个引用队列:java.lang.ref.Finalizer.ReferenceQueue.

Finalizer对象怎么被清理的呢

JVM默认会创建一个finalizer线程来处理Finalizer对象,如果你去抓取线程堆栈的话可以看到这个线程的堆栈,如下所示:

1

2

3

4

5

6

7

8″Finalizer” daemon prio=10 tid=0x0962d000 nid=0x4836 runnable [0xafaa8000]

java.lang.Thread.State: RUNNABLE

at java.lang.Thread.yield(Native Method)

at finalizer.finalize(finalizer.java:5)

at java.lang.ref.Finalizer.invokeFinalizeMethod(Native Method)

at java.lang.ref.Finalizer.runFinalizer(Finalizer.java:83)

at java.lang.ref.Finalizer.access$100(Finalizer.java:14)

at java.lang.ref.Finalizer$FinalizerThread.run(Finalizer.java:160)

这个线程唯一的职责就是不断的从java.lang.ref.Finalizer.ReferenceQueue队列中取对象,当一个对象进入到队列中,finalizer线程就执行对象的finalize方法并且把对象从队列中删除,因此在下一次GC周期中可以看到这个对象和Finalizer对象都被清除了.

大部分场景finalizer线程清理finalizer队列是比较快的,但是一旦你在finalize方法里执行一些耗时的操作,可能导致内存无法及时释放进而导致内存溢出的错误,在实际场景还是推荐尽量少用finalize方法.

实战案例1

2

3

4

5

6

7

8

9

10

11

12

13

14public class Finalizer{

@Override

protected void finalize() throws Throwable{

System.out.println(“finalize”);

}

public static void main(String str[]) throws IOException{

for (int i = 0; i < 10000; i++) {

Finalizer force = new Finalizer();

}

//让线程阻塞住,方便分析内存使用情况

System.in.read();

}

}

执行main方法后使用jmap命令查看内存使用情况,可以看到java.lang.ref.Finalizer和Finalizer的实例都创建了10000个:

1

2

3

4

5

6

7

8

9

10

11

12

13

14$ jmap -histo 8700|head -n 10

num #instances #bytes class name

———————————————-

1: 646 3398408 [I

2: 1851 1511144 [B

3: 6081 808864 [C

4: 10175 407000 java.lang.ref.Finalizer

5: 10000 160000 Finalizer

6: 4328 103872 java.lang.String

7: 601 64208 java.lang.Class

8: 683 40952 [Ljava.lang.Object;

9: 785 31400 java.util.TreeMap$Entry

10: 248 14144 [Ljava.lang.String;

接下来使用jmap -histo:live 8700|head -n 10命令强制触发一次GC,结果和前面的分析一致,Finalizer对象都放到引用队列中,并依次调用了对象的finalize方法,内存中java.lang.ref.Finalizer和Finalizer对象依然存在,不过这一java.lang.ref.Finalizer

不再引用Finalizer对象,下一次GC周期时两者都属于垃圾对象:

1

2

3

4

5

6

7

8

9

10

11$ jmap -histo:live 8700|head -n 10

num #instances #bytes class name

———————————————-

1: 10175 407000 java.lang.ref.Finalizer

2: 3043 372608 [C

3: 605 273624 [B

4: 10000 160000 Finalizer

5: 2883 69192 java.lang.String

6: 601 64208 java.lang.Class

7: 631 37008 [Ljava.lang.Object;

再触发一次jmap -histo:live 8700|head -n 10,可以看到两者都被回收了:

1

2

3

4

5

6

7

8

9

10

11$ jmap -histo:live 8700|head -n 10

num #instances #bytes class name

———————————————-

1: 3059 373224 [C

2: 498 138064 [B

3: 2899 69576 java.lang.String

4: 602 64312 java.lang.Class

5: 631 37008 [Ljava.lang.Object;

6: 785 31400 java.util.TreeMap$Entry

7: 227 11256 [Ljava.lang.String;

最后我们来总结一下:

finalize对象至少经历两次GC才能被回收,因为只有在FinalizerThread执行完了finalize对象的finalize方法的情况下才有可能被下次GC回收,而有可能期间已经经历过多次GC了,但是一直还没执行finalize对象的finalize方法;

CPU资源不足的场景FinalizerThread线程可能因为优先级较低而一直没有执行对象的finalize方法,可能导致大部分对象进入到老年代,进而触发老年代GC,设置触发Full GC.

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/192019.html原文链接:https://javaforall.net

(0)
上一篇 2026年1月20日 下午8:43
下一篇 2026年1月20日 下午9:22


相关推荐

  • numpy安装过程

    numpy安装过程下载 numpy 库的详细方法安装前提 1 Python3 8 安装完成并能够正常使用 2 下载相应的 numpy 安装包 whl 格式安装详细步骤 首先 点击下面的链接进入 numpy 下载官网链接地址 添加链接描述进去之后 点击文档下载 进入界面 cp37 指对应的 Python 版本 还要注意后面的 32 64 是你的系统然后点击下载 下载到你 Python 下面的 Scripts 文件夹下 打开 cmd 输入命令行 pipinstallnu 文件名 whl 出现 Successfulin

    2025年9月7日
    6
  • 万能量产工具_u盘量产工具怎样使用

    万能量产工具_u盘量产工具怎样使用MPALLv3.23.00版本量产工具可以用来给16G金士顿U盘(PS2251-50)主控刷固件量产。当然也适合其他ps22XX主控的量产只是不用刷固件,一般是量产坏了或者量产不了才涉及到刷固件。做设置分区数量,制作u盘启动盘、低级格式化等不需要刷固件即可成功量产。包含固件为50的固件:BN50V313M-8K.BIN BN50V3122KM-2@4k.BIN FW50FF01

    2025年10月12日
    5
  • Python电影评论数据分析系统_echarts数据可视化

    Python电影评论数据分析系统_echarts数据可视化书接上文,继上文实现了《复仇者联盟4:终局之战》电影的影评数据分析采集之后,本文主要对获取到的影评数据进行一些可视化展示,主要的可视化展示手段是词云。这里我把200条左右的纯评论数据抽取解析了出来,贴在下面,感兴趣的可以拿去:=========================================================================…

    2025年11月6日
    9
  • Cover Letter常用范式和模版

    Cover Letter常用范式和模版摘自:https://zhuanlan.zhihu.com/p/26708261;http://muchong.com/html/201401/6920446.html1.什么是Coverletter?CoverLetter,即投稿信,是论文投递时与论文一起发送给编辑的信件,其目的是让编辑在阅读你的论文之前,简单了解你文章的基本情况。Coverletter是编辑对论文的第一印象,也是初步评判你论文是否可以被期刊接收的重要依据(如果编辑看完Coverletter之后一点兴趣也没有,就没有下文了

    2022年5月6日
    82
  • 深入了解HDMI接口

    深入了解HDMI接口版权声明 本文为芝麻软件工作室原创文章 未经芝麻软件工作室允许不得转载 一 HDMI 接口的工作原理这张图是 HDMI 接口的架构示意图 从左边的信号源中你可以看到 HDMI 接口的信源可以是任何支持 HDMI 输出的设备 而接入端也可以是任何带有 HDMI 输入接口的设备 无论他们是音频设备 视频设备还是控制设备 HDMI 接口都可以应用其中 在 HDMI 接口中的数据信号采用的是 TMDS 最小化

    2026年3月16日
    2
  • VS2010安装失败

    我的情况和http://blog.csdn.net/gtatcs/article/details/8770025类似:安装提示错误:然后 尝试单独安装.net4.0.NETFramework4.0安装失败原因是HRESULT0xc8000222,截图如下:搜索“.netframework4.00xc8000222”的解决办

    2022年4月7日
    47

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号