前言
话说在最前面:finalize方法的用途有两个:1.finalize()方法释放本地方法申请的内存;2.作为终结条件
Java中有垃圾回收,可以对new出来且不再使用的对象进行回收,
但是Java中有些情况下,申请内存不同通过new进行的!这种情况下垃圾回收就无法进行内存回收了
这种时候finalize()方法就会发挥它的作用,对这部分特殊申请的内存进行内存回收处理!
了解finalize之前需要知道三点
- Java中的对象不想C++中的对象,是可能不被垃圾回收的;(如果C++的代码没问题,申请的内存是一定要释放的,但是Java中的对象如果没达到条件,是不会被GC的)
- Java中的垃圾回收不等于C++中的析构函数;(Java并没有提供相关析构函数的概念,垃圾是自动回收的,不需要手动创建一个执行清理工作的方法)
- 垃圾回收只与内存有关;(Java中的垃圾回收只针对不再使用的内存,因此垃圾回收的任何行为(以及finalize方法)都必须只跟内存回收有关)
(用途1)finalize()方法释放本地方法申请的内存
- finalize方法涉及到一种特殊的申请对象存储空间的方式(并不是一般的通过new来申请)。
- 这种情况发生在Java中使用本地方法的时候(因为本地方法使用的是C或者C++来实现的),在本地方法中,C语言会调用malloc()函数来申请分配存储空间,而且除非调用了free()函数,不然是不会释放内存空间的(会导致内存泄漏)。
- 因此在finalize()方法中进行调用了本地方法中的(C语言的)free()方法释放了malloc申请的内存空间。
gc和finalize都不保证一定发生
- 在C++中,如果创建了一个局部对象,那么这个局部对象会存储在栈中,跟随方法的结束而销毁;C++如果通过new来创建对象,当使用结束的时候需要通过delete方法来调用析构函数,来讲new出来的内存空间释放掉,不过没有delete方法,那么析构函数永远不会被调用,那么就导致了内存泄漏。
- 相反,Java中首先没有局部对象的概念,在HotSpot虚拟机中没有栈上分配的概念,因此对象都是在堆中通过new的方式申请的内存;其次Java中没有析构函数和delete的概念,但是Java有GC垃圾回收器。这样的话就比较方便了,GC会在Java运行时内存面临耗尽的情况下,对内存空间进行垃圾回收或者finalize。
- 这样也就说明了,垃圾回收和finalize并不是一定能发生的,如果内存状况良好,或者没有垃圾,就可能不发生gc和finalize
(用途2)finalize作为终结条件
- finalize方法绝对不可以直接调用,因此可能使用finalize方法的情景就少了,但是finalize还有别的用处!
- 也就是将finalize方法当做对象的终结条件,进行验证!
- 举个例子,一个对象如果我们不使用了,我们希望的是它能够被gc掉,但是可能这个对象中会存在一些调用,导致这个对象并不能被当做垃圾顺利的gc掉,太多的话会导致内存在漏严重,并且往往这些代码上的缺陷非常的难发现,这个时候就可以使用finalize方法来发现这种现象是否存在。
- 尽管finalize方法不能被显式的调用,但是我们能够通过finalize方法作为检查对象终结的条件,发现代码上的缺陷,找到问题所在!这个用法很关键!
举个代码上的例子:
- 假设每个被new出来的Solution对象被垃圾回收的终结条件是:status为false;(说人话就是对象的status为false才会被gc,如果为true的话就不会被gc)
- 在mian方法中,new了两个对象,其中一个将status改为了false,但是另一个假如忘记修改状态了。
- 这个时候finalize作为终结条件,就可以发现这个问题(发现要gc的对象没有更改状态,并打印)(如下代码所示)
class Solution {
boolean status = false;
public Solution(boolean status) {
this.status = status;
}
public void setStatusFalse(){
this.status = false;
}
//这里重写了finalize方法
@Override
protected void finalize() throws Throwable {
if (this.status){
System.out.println("error:内存泄漏");
}
}
public static void main(String[] args) {
Solution solution = new Solution(true);
solution.setStatusFalse();
new Solution(true); // 忘记更改状态
System.gc(); //强制进行full gc
}
}
输出:
这个时候我们就可以根据打印的输出,检查一下是不是写代码有bug,导致有对象并没有达到终结条件。
【end】