java中为什么不推荐使用finalize,知道原因后相信你也不会用了

简介: java提供了一个finalize方法,可以帮助我们进行资源释放,类似于C++中的析构函数。但是目前普遍的认识是不要使用,为什么呢?就是因为对java虚拟机的垃圾回收有影响。这篇文章对其进行一个说明。

一、为什么有影响


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


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

v2-9d700b1e9d4408ebf6186ff962e84345_1440w.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,进入了队列但一直没有被调用的这段时间,会一直占用内存。


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


二、案例演示


我们创建一个类

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.<init>(TestFinalizer.java:6)
    at com.fdd.chapter2.TestFinalizer.main(TestFinalizer.java:14)

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

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

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

v2-b14601fc6d8cf88a9b42756c3449b5a3_1440w.jpg

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

v2-fd1b0c661b139aecde555a77ea8a86c1_1440w.jpg


相关文章
|
1月前
|
Java API
【JAVA】final、finally、finalize 有什么区别?
【JAVA】final、finally、finalize 有什么区别?
|
1月前
|
Java
面试官:小伙子来说一说Java中final关键字,以及它和finally、finalize()有什么区别?
面试官:“小伙子,用过final关键字吗?” 我:“必须用过呀” 面试官:“好,那来说一说你对这个关键字的理解吧,再说一说它与finally、finalize()的区别” 我:“好嘞!
22 1
|
1月前
|
设计模式 缓存 算法
史上最全Java面试题:数据结构+算法+JVM+线程+finalize+GC
基本概念 操作系统中 heap 和 stack 的区别 什么是基于注解的切面实现 什么是 对象/关系 映射集成模块 什么是 Java 的反射机制
|
11月前
|
Java
【Java面试】说说你对finalize的理解
【Java面试】说说你对finalize的理解
39 0
Java 最常见的面试题:final、finally、finalize 有什么区别?
Java 最常见的面试题:final、finally、finalize 有什么区别?
|
Java C++
【Java技术专题】「原理专题」深入分析Java中finalize方法的作用和底层原理
【Java技术专题】「原理专题」深入分析Java中finalize方法的作用和底层原理
152 0
【Java技术专题】「原理专题」深入分析Java中finalize方法的作用和底层原理
|
Java 程序员
Java基础Object类的finalize方法
Java基础Object类的finalize方法
Java基础Object类的finalize方法
|
Java 编译器 数据库连接
【Java】Java中的final、finally、 finalize有什么不同?
Java中的final、finally、 finalize有什么不同?
99 1
|
设计模式 XML 缓存
2022年史上最全Java面试题:数据结构+算法+JVM+线程+finalize+GC
基本概念 操作系统中 heap 和 stack 的区别 什么是基于注解的切面实现 什么是 对象/关系 映射集成模块 什么是 Java 的反射机制 什么是 ACID BS与CS的联系与区别 Cookie 和 Session的区别 fail-fast 与 fail-safe 机制有什么区别 get 和 post请求的区别 Interface 与 abstract 类的区别 IOC的优点是什么 IO 和 NIO的区别,NIO优点 Java 8 / Java 7 为我们提供了什么新功能 .......