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.

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

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

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)


相关推荐

  • 哈佛幸福课笔记上篇「建议收藏」

    哈佛幸福课笔记上篇「建议收藏」改变一生的课:哈佛幸福课笔记上篇第1课什么是积极心理学?第2课为什么要学习积极心理学?第3课幸福是一种随机现象吗?第4课积极的环境能改变人第5课环境的力量第6课乐观主义第7课逆境还是机遇?第8课感激链接:哈佛大学公开课:幸福课.《哈佛幸福课》是改变我生活最大的一项事物,没有之一。我学习了5遍幸福课,并且用过去6年的时间去尝试它践行它,感觉完全改变了我的生活。第1课什么是积极心理学?1.享受安静2.这门课不光是传授信息,而且关于如何变形。重要的不仅仅是获得了什么信息,还是何形状

  • java怎么键盘录入字符串

    java怎么键盘录入字符串-java怎么键盘录入字符串如图所示:来看一下每句代码的意思:先写上:importjava.util.Scanner//导入java.util包下的Scanner类,导入后才能使用它接着:Scanner.sc=newScanner(System.in);//new是指创建了一个scanner类,在创建Scanner类的对象时要用System.in作为…

  • 简单介绍T检验和卡方检验「建议收藏」

    简单介绍T检验和卡方检验「建议收藏」最近在看统计学方面的知识,正好有个学妹问我一些检验方面的东西,以前读书那会的统计学知识早已忘记,经过半天的努力,又把知识给拾起来了,下面简单介绍下T检验和卡方检验。1. T检验适用范围:主要用于样本含量较小(例如n总体标准差σ未知的正态分布。其中最常用的是单总体t检验,单总体t检验是检验一个样本平均数与一个已知的总体平均数的差异是否显著。当总体分布是正态分布,如总体标准差未知

  • 【uboot】imx6ull uboot移植LAN8720A网卡驱动

    【uboot】imx6ull uboot移植LAN8720A网卡驱动文章目录相关文章1.前言2.IMX6ULLEthernetLAN8720A硬件连接3.支持LAN8720A修改步骤4.验证测试问题1:如何确定LAN8720A网卡PHYAD地址?问题2:如何确定devicetree中对resetgpio的定义?问题3:LAN8720A网卡nINTSEL是如何配置?问题4:IMX6ULLETH是如何被初始化的?相关文章1.《【uboot】imx6ulluboot2020.04源码下载和编译环境配置》2.《【Ethernet】以太网卡LAN8720

  • DHCP option82字段「建议收藏」

    DHCP option82字段「建议收藏」DHCPoption82字段的添加1.添加option82字段1.添加option82字段option82是dhcp报文中的中继代理信息选项(relayagentinformationoption),抓取上行的dhcp报文,根据目的端口为67的报文进行处理,解析报文,在原始报文的基础上添加option82字段,添加之后进行ip,udp的重新校验值的计算,然后发往PON口。温馨提示:以上文章描述如有不清晰之处,欢迎在评论区评论,如有时间,会第一时间回复,谢谢!…

    2022年10月16日
  • 51单片机IIC通信协议

    51单片机IIC通信协议/*——————————————————————————*@fileI2C.H*@authorByron(from3900@gmail.com)*@versionV1.0.0*@date05/12/2020*@brief51系列单片机I2C通信协议头文件*——————————————-

发表回复

您的电子邮箱地址不会被公开。

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