【腾讯云】境外1核2G服务器低至2折,半价续费券限量免费领取!

搜搜吧

查看: 95|回复: 0

一个GCRoot不可达的对象,会立刻被垃圾回收吗?

[复制链接]

超级钻石贵宾会员

1万

主题

2万

帖子

5万

积分

Rank: 1

UID
15343
威望
-561
贡献
8107
在线时间
381 小时
注册时间
2015-10-12
发表于 2021-1-12 16:25:10 | 显示全部楼层 |阅读模式

78b662751555d328608cdf3f3cfa8ce7.jpg-wh_651x-s_97369169.jpg

这个问题是我在刷牛客面经的时候遇到的,还特地整理在了我的常规面试题文档中,所以这道题主要考察的就是finalize方法的影响。

java提供了一个finalize方法,可以帮助我们进行资源释放,类似于C++中的析构函数。这篇文章对其进行一个说明。

一、为什么有影响

我们都知道一个对象GCRoot不可达,java虚拟机就认为是垃圾对象,就会进行垃圾回收,但是如果这个对象包含了finalize函数,性质就不一样了。怎么不一样了呢?

java虚拟机在进行垃圾回收的时候,一看到这个对象类含有finalize函数,就把这个函数交给FinalizerThread处理,而包含了这个finalize的对象就会被添加到FinalizerThread的执行队列,并使用一个链表,把这些包含了finalize的对象串起来。

834cf5dc08fdc0b47a3864c0d2dbbb1d.jpg-wh_600x-s_637264553.jpg

他的影响在于只要finalize没有执行,那么这些对象就会一直存在堆区,不过这里只是4个包含了finalize的对象,影响不是那么大,如果有一万个或者是十万个呢?这就影响大了。

finalize的原理其实很简单,在这里简要的梳理一下:

(1)对象在初始化的过程中会判断是否重写了finalize,方法是判断两个字段标志has_finalizer_flag和RegisterFinalizersAtInit。

(2)如果重写了finalize,那就把当前对象注册到FinalizerThread的ReferenceQueue队列中。注册之后的对象就叫做Finalizer。方法是调用register_finalizer函数。此时java虚拟机一看当前有这个对象的引用,于是就不进行垃圾回收了。

(3)对象开始被调用,FinalizerThread线程负责从ReferenceQueue队列中获取Finalizer对象。开始执行finalize方法,在执行之前,这个对象一直在堆中。

(4)对象执行完毕之后,将这个Finalizer对象从队列中移除,java虚拟机一看对象没有引用了,就进行垃圾回收了。

这就是整个过程。不过在这里我们主要看的是finalize方法对垃圾回收的影响,其实就是在第三步,也就是这个对象含有finalize,进入了队列但一直没有被调用的这段时间,会一直占用内存。

注意:这里其实就是一道面试题,我在看牛客网上的面经时,看到有人被问到过。也就是GCRoot不可达的对象,会立刻被垃圾回收吗?

我们使用一个案例来分析一波:

二、案例演示

我们创建一个类

  • public class TestFinalizer {
  •     public static class Fdd {
  •      //分配1M
  •         private byte[] content = new byte[1024*1024];
  •         @Override
  •         protected void finalize() {
  •             System.out.println("finalize被执行");
  •         }
  •     }
  •     public static void main(String[] args) {   
  •         for (int i = 0; i < 1000; i++) {
  •          Fdd fdd = new Fdd();  
  •         }
  •     }
  • }

现在创建了类,我们设置一下参数。

  • # 最大堆内存
  • -Xmx5m  
  • # 最小堆内存
  • -Xms5m   
  • # 堆内存溢出错误打印
  • -XX:+HeapDumpOnOutOfMemoryError  
  • # 把堆相关信息保存在下列路径
  • -XX:HeapDumpPath=F:/a.dump

在main方法中,创建了1000个Fdd对象,如果不执行finalize方法,那么因为没有调用所以会进行垃圾回收,此时不断我们创建多少个,都不会出现任何问题。但是如果存在finalize方法,就不一样了。

  • java.lang.OutOfMemoryError: Java heap space
  • Dumping heap to F:/a.dump ...
  • finalize被执行
  • finalize被执行
  • finalize被执行
  • finalize被执行
  • finalize被执行
  • finalize被执行
  • finalize被执行
  • Unable to create F:/a.dump: File exists
  • Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
  •     at com.fdd.chapter2.TestFinalizer$Fdd.(TestFinalizer.java:6)
  •     at com.fdd.chapter2.TestFinalizer.main(TestFinalizer.java:14)

我们看到每个对象都会执行finalize,在执行之前的这段时间一直会在堆区,执行完了就会被清理,所以你看到这里执行了不少于5次的finalize方法。但是对象一旦超出了我们设置的5M,就会出现内存溢出。一句话总结就是出现了对象堆积。现在使用MAT工具来分析一下。

Mat工具是一个插件,也可以自己下载一个。下载完成之后打开我们刚刚生成的a.dump即可。

下面这张图就是分析的结果:

18f225051ecda8450c16c06f64acf3a2.jpg

a这块的内容就是Finalizer,也就是我们的Fdd对象,b包含的比较多,乱七八糟的剩余信息。当然你也可以查看一些其他的信息。都在MAT工具上。还有一些正在执行的finalizer和准备执行的。

0627d799d7822b6701ce379764b8759c.jpg-wh_600x-s_561981122.jpg

OK,一些其他的信息就不再展示了。

结论

一个GCRoot不可达的对象,不会立刻被垃圾回收,首先还会判断是否包含了finalize方法,如果有那就先执行finalize方法,如果这样的对象比较多,那么这部分对象及时GCRoot不可达,变得没用了,也会留在内存中,影响程序的效率。

本文转载自微信公众号「愚公要移山」,可以通过以下二维码关注。转载本文请联系愚公要移山公众号。


过年了,祝各位新年快乐
Powered by www.sosoba.org Copyright © 2013-2021 搜搜吧社区 小黑屋|手机版|Archiver|地图|联系站长|腾讯云代金券|搜搜影视|seo优化服务|搜搜吧
广告服务/项目合作/会员购买:QQ 侵权举报邮箱: fuwu-sosoba@qq.com  搜搜吧建站时间:创建于2013年07月23日
免责声明:本站所有的内容均来自互联网以及第三方作者自由发布,版权归原作者版权所有,搜搜吧不承担任何的法律责任,若有侵权请来信告知,我们立即删除!
版权声明:搜搜吧影视资源均收集自互联网,没有提供影片资源存储和下载,也未参与录制上传,若本站收录的资源涉及您的版权或知识产权或其他利益,我们会立即删除

GMT+8, 2021-1-23 06:36 , Processed in 0.015607 second(s), 11 queries , MemCached On.

快速回复 返回顶部 返回列表