重温经典《Thinking in java》第四版之第五章 初始化与清理(三十)

简介: 重温经典《Thinking in java》第四版之第五章 初始化与清理(三十)

5.5 清理:终结处理和垃圾回收,在了解了初始化的重要性以后,就要了解清理的重要性了。在使用程序库的时候,把一个对象使用完以后就“弃置不顾”的做法并非总是安全的。一般情况下,Java的垃圾回收器负责回收无用对象占用的内存。但是对于那些不是经过new出来的对象,垃圾回收器不知道如何释放这块特殊的内存。Java提供了finalize方法,它的工作原理是这样的:一旦垃圾回收器准备好释放对象占用的存储空间,将首先调用其finalize方法,并且在下次垃圾回收动作发生时,才会真正回收对象占用的内存。

对于这一点,经常被做C++的工程师吐槽。有些C++转到Java的工程师在一开始做Java开发的时候,往往把finalize方法错认为是析构函数的作用,认为资源肯定会被立马释放。其实这里面需要关注两个困扰:

1、对象可能不被垃圾回收,就算被回收,可能不会立刻发生

2、垃圾回收并不等于析构

牢记上面两点,即可远离困扰。意思是说我们不能依赖finalize函数做一些资源释放的操作,你可以理解为finalize函数是一种可能发生可能不发生的函数。所以对于一些重要的资源内存区域,甚至所有的资源内存区域,我们必须自己收到创建一个执行清理工作的方法。

也许你会发现,只要程序没有濒临存储空间用完的那一刻,对象占用的空间就总也得不到释放。如果程序执行结束,并且垃圾回收器一直都没有释放你创建的任何对象的存储空间,则随着程序的退出,那些资源也会全部交还操作系统。这个策略是恰当的,因为垃圾回收本身也有开销,要是不使用它,那就不用支付这部分开销了。

那么finalize的用途何在呢?

请记住:垃圾回收只与内存有关。也就是说,使用垃圾回收器的唯一原因是为了回收程序不再使用的内存。这里的程序不再使用的内存是指通过Java语言层面传统的方式new出来或者是创建出来的对象占用的内存,垃圾回收器会负责回收。如何回收以及回收的时间点,回收多少都是垃圾回收器内部机制决定的,有专门的书籍或者篇章会去讲垃圾回收器。那什么样的内存不是垃圾回收器负责回收的呢,举个例子,Java中调用本地方法,比如c/c++写的方法,它产生的内存,垃圾回收器就不会收。那么垃圾回收器负责回收的内存区域叫什么呢,在JVM内存模型中叫堆,这个堆以外的就是他不负责回收了。

虽然finalize并不是总是被调用,但是一旦被finalize被调用了,而我们在finalize方法中实现了检查对象中存在没有被适当清理的部分,那么我们就可以借助这种方式找到程序中问题的缺陷,这才是我们关注的。比如下面的例子:

classBook { 
booleancheckedOut=false; 
Book(booleancheckOut) { 
checkedOut=checkOut; 
    } 
voidcheckIn() { 
checkedOut=false; 
    } 
protectedvoidfinalize() { 
if(checkedOut) 
System.out.println("Error: checked out"); 
// Normally, you’ll also do this: // super.finalize(); // Call the base-class version         } 
    }
}
publicclassTerminationCondition { 
publicstaticvoidmain(String[] args) { 
Booknovel=newBook(true); 
// Proper cleanup: novel.checkIn(); 
// Drop the reference, forget to clean up: newBook(true); 
// Force garbage collection & finalization: System.gc(); 
    } 
}

/* Output:

Error: checked out

*///:~

本例子的终结条件是:所有的Book对象再被当作垃圾回收前都应该被签入。但是在main方法中,由于程序员的错误,有一本书未被签入,要是没有finalize方法来验证终结条件,将很难发现这种缺陷。最后还是建议大家,尽量少用finalize吧。

目录
相关文章
|
2月前
|
安全 Java 关系型数据库
Java连接Mysql SSL初始化失败
Java连接Mysql SSL初始化失败
|
2月前
|
存储 Java
如何在 Java 中初始化二维 ArrayList
【8月更文挑战第23天】
58 1
|
2月前
|
存储 JavaScript Java
Java中未被初始化的字符串打印出“null”?
在Java中,未初始化的`String`变量默认值为`null`。打印此类变量时输出“null”,是因为`PrintStream`类中的`print`方法特别处理了`null`值,将其转换为字符串“null”。从JDK 17开始,`println`方法通过`String.valueOf`间接实现相同功能。当拼接包含`null`的字符串时,如`s1 + "BLACK"`,结果为“nullBLACK”,这是因为字符串构建过程中`StringBuilder`的`append`方法将`null`转换为“null”。
|
2月前
|
存储 Java 编译器
如何在 Java 中初始化对象 Arraylist?
【8月更文挑战第23天】
65 0
|
2月前
|
存储 Java 开发工具
【Azure Developer】VS Code运行Java 版Azure Storage SDK操作Blob (新建Container, 上传Blob文件,下载及清理)
【Azure Developer】VS Code运行Java 版Azure Storage SDK操作Blob (新建Container, 上传Blob文件,下载及清理)
|
2月前
|
Java Spring
Java SpringBoot Bean InitializingBean 项目初始化
Java SpringBoot Bean InitializingBean 项目初始化
41 0
|
2月前
|
Java
Java中 字符串,字符串数组,整型数组初始化
Java中 字符串,字符串数组,整型数组初始化
10 0
|
3月前
|
NoSQL Java Redis
软件开发常见流程之宝塔初始化安装环境配置,Lam前面不选,直接跳商城,在宝塔内点击软件商城,安Mysql5.7,安java项目管理器,安Ngnix最新版,安Redis
软件开发常见流程之宝塔初始化安装环境配置,Lam前面不选,直接跳商城,在宝塔内点击软件商城,安Mysql5.7,安java项目管理器,安Ngnix最新版,安Redis
|
3月前
|
存储 Java 容器
Java数组的初始化方法
Java数组的初始化方法
|
4月前
|
缓存 并行计算 Java
重温JAVA线程池精髓:Executor、ExecutorService及Executors的源码剖析与应用指南
重温JAVA线程池精髓:Executor、ExecutorService及Executors的源码剖析与应用指南
下一篇
无影云桌面