前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
社区首页 >专栏 >【周一电台】finalize移除在即,你用什么清理资源?

【周一电台】finalize移除在即,你用什么清理资源?

作者头像
翊君
发布于 2022-03-15 12:02:47
发布于 2022-03-15 12:02:47
47300
代码可运行
举报
文章被收录于专栏:周一电台周一电台
运行总次数:0
代码可运行

0. 学完本文你将会学到

  • finalize()方法是什么
  • 三种清理资源方案以及优缺点1. 前言 在JEP 421中,JDK 18已经明确表示将会移除finalize()方法。

2. finalize是什么?

在跟finalize说bye bye并且找到它的代替品之前,让我们先来了解下finalize到底是什么?

finalize()方法是Object类中提供的一个方法,在GC(Garbage Collection)准备释放对象所占用的内存空间之前,它将会首先调用对象的finalize()方法。

Java中,finalize()方法主要用来释放非资源(比如打开的文件资源、数据库连接等)。

Java中的每一个对象都有一个finalize()方法,每个对象可以参与到关闭资源的机制中来。这也解决了抛出异常以及其他清理代码可能会被遗漏的情况——只要在你的资源消耗的对象上覆盖finalize()方法就行了。

这个想法不错,但是实际又是完全另一回事,finalize有很多让人非常头疼的缺点。如下所示:

  • finalize()方法的调用时机具有不确定性,它可能永远也不会调用,从一个对象变得不可到达开始,到finalize()方法被执行,所花费的时间是难以预计的。而且因为Java的垃圾回收器的特性,我们不能依赖finalize()方法能及时地回收占用的资源。可能出现的情况是在我们耗尽资源之前,GC仍未触发,因此我们常常手动close。
  • finalize()方法可以误使本来已经死亡的类复活。设想这样一个情况,一个对象引发了一个异常,使它有进入GC。然而,finalize()方法首先得到运行的机会,该方法可以做任何事情,包括重新建立对该对象的实时引用。这是一个潜在的泄漏和安全的隐患。
  • finalize()方法是很难正确实现的。如果只是写一个看似可靠的finalize()方法,那么不能保证其想要实现的功能。特别是对于finalize()方法的线程影响,难以保证。
  • finalize()方法存在一定的性能问题。因为finalize()方法在实现它既定目的时不可靠,所以JVM在支持它上面所花费的开销是不值得的。
  • finalize()方法使得大规模应用更加脆弱,并且在遇到严重负载的情况下会出现难以恢复的错误情况。

3. finalize()方法的替代方案

让我们回顾下处理异常以及关闭资源的常用操作。

这里准备了三种替代方案:

  • try-catch-finally
  • try-catch-resource
  • cleaner3.1 try-catch-finally 这个释放资源的方式应该是很常见的了。它在很多情况下是可行的,但是它也会有缺点——容易出错并且冗长。

关闭资源的时候有可能也会引发另外一个异常。在一个长期运行和重度使用的系统中,这种情况会产生资源泄露,从而扼杀一个应用程序。

而且这种粗暴的做法我们不得不在整个代码库中重复。

请看下面这个例子:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
FileOutputStream outStream = null;
try {
    outStream = new FileOutputStream("output.txt");
    ObjectOutputStream stream = new ObjectOutputStream(outStream);
    . . .
    stream.close();
} catch(FileNotFoundException fe) {
    System.out.println("Can't find the file");
} catch(IOException ioe) {
    System.out.println("Can't write to file");
} finally {
    if (outStream != null) {
        try {
            outStream.close();
    } catch (Exception e) {
        System.out.println("Can't close the stream", e);
    }
  }
}

在这个例子里面,你要做的就是打开一个流,向它写进一些字节,然后保证无论抛出什么异常,它被关闭即可。

为了做到这一点,你不得不把这些代码放在一个try代码块中,然后在catch代码块中处理异常。你还需要添加一个finally块,对流进行double check。为了保证一个可能的异常不会阻止流的关闭,你不能直接关闭流,你还要用另一个try块将其包裹。

听上去就很繁琐,不是吗?就算你会熟练使用ctrl c和ctrl v,难免也会感到困惑。

这对一个简单而且普遍的需求来说,这么冗长啰嗦的代码确实让人心烦。

所以我们继续往下看看其他的解决方案。

3.2 try-with-resource

try-with-resource是在Java 7中引进的,所以对仍在使用JDK8的大多数用户来说,忘掉try-catch-finally吧。

try-with-resource语句允许你指定一个或多个资源对象作为try声明的一部分。这些资源保证在try块完成时被关闭。

更进一步来说,任何实现java.lang.AutoCloseable的类都可以用在try-with-resource语句中。

现在让我们用try-with-resource重写上一小节的例子,请看:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
try (FileOutputStream outStream = new ObjectOutputStream(outStream)) {
    ObjectOutputStream stream = new ObjectOutputStream(outStream);
    stream.write //…
    stream.close();
} catch(FileNotFoundException fe) {
    System.out.println("Can't find the file");
} catch(IOException ioe) {
    System.out.println("Can't write to file");
}

显而易见,代码量减少了。但是最大的好处是,一旦你将try块括号内声明资源交给JVM,你将不会再担心它会产生资源泄露。

在这个例子中,我们已经消除了finally块。然后在有些情况下,我们需要一个更加强大的解决方案。

当情况更为复杂而这样单一的代码块无法满足我们需求时,我们需要一个Cleaner。

3.3 Cleaner

Cleaner类是在Java 9中引入的,用于管理一组对象引用和相应的清理操作。

Cleaner的想法是将清理程序与需要清理的对象的代码脱钩。

让我们看看这个Oracle文档中的例子:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public class CleaningExample implements AutoCloseable {
    // A cleaner, preferably one shared within a library
    private static final Cleaner cleaner = <cleaner>;
    static class State implements Runnable {
    State(...) {
      // initialize State needed for cleaning action
    }
    public void run() {
      // cleanup action accessing State, executed at most once
    }
  }
    private final State;
    private final Cleaner.Cleanable cleanable
    public CleaningExample() {
        this.state = new State(...);
        this.cleanable = cleaner.register(this, state);
    }
    public void close() {
        cleanable.clean();
    }
}

与finalize()方法不同的是,你可以明确地调用close()方法来清理你的对象引用。而finalize()方法完全依赖于来自GC的不确定性的调用。

如果没有明确地调用close()方法,作为cleaner.register()方法的第一个参数成为幻象可达的状态时,系统将为你执行它。然而,如果明确地执行了close()方法,系统就不会再调用它。

本节中的代码产生了一个AutoCloseable对象。这表示它可以被传递到try-with-resource语句的参数中。

现在我们使用Cleaner还要注意的是,不要在Cleaner的运行方法中创建已清理对象的引用,因为这样做会创建一个僵尸对象。

此外,我们可以看到cleaner是static的,因为每个Cleaner都会产生一个线程,所以共享Cleaner将导致运行程序的开销降低。

此外,我们还可以注意到被监控的对象与执行清理工作的代码(在本例中是State)是解耦的。

关于Cleaner机制,《Java编程思想》提倡避免使用它。认为它同样在不确定性和性能问题上存在缺陷,这点在本文中不再讨论,感兴趣的读者可移步此处

感谢收看本期的翊君@周一电台。如果你觉得还不错的话,快给我三连支持一下吧,咱们下期不见不散呐。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2022/02/07 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
final 、finally、finalize有什么区别
finall 是Java中保证代码一定要被执行的一种机制,我们可以使用 try-finally 或者 try-catch-finally 来进行类似关闭JDBC,unlock 锁等动作。
王小明_HIT
2019/11/04
8330
Java 中的 final、finally、finalize 有什么不同?
Java 中 final、finally、finalize 有什么不同?这是在 Java 面试中经常问到的问题,他们究竟有什么不同呢?
武培轩
2019/10/29
8880
Java 中的 final、finally、finalize 有什么不同?
【面试精讲】Java:final、finally 和 finalize 有什么区别?
Java 语言有很多看起来很相似,但是用途却完全不同的语言要素,谈谈 final、finally、 finalize 有什么不同?
sidiot
2023/08/31
1890
try 语句如何更优雅的关闭资源?请看这里!
try-with-resources是 JDK 7 中引入的一个新的异常处理机制,它能让开发人员不用显式的释放try-catch语句块中使用的资源。
Java极客技术
2022/12/04
3860
你还在使用 try-catch-finally 关闭资源?
作者:何甜甜在吗 链接:https://juejin.im/post/5b8f9fa05188255c6f1df755
Java技术栈
2020/07/03
8650
不会吧,还有人在 finally 里关闭资源?
身为 Java 大神的各位肯定避免不了使用各式各样需要关闭的 Stream 或 Client 吧。例如 FileInputStream、HTTPClient 之类的。那么大家肯定会遇到以下恶心的代码
Java程序猿阿谷
2020/11/12
5770
不会吧,还有人在 finally 里关闭资源?
final 、finally finalize 有什么不同?
final 可以用来修饰类、方法、变量。final 修饰的 class 代表不可以继承扩展,final 修饰的方法标识不能被重写(override),final 修饰的方法表示不可以修改。
王小明_HIT
2020/05/11
7930
谈谈fnal、fnally、 fnalize有什么不同?
Java语言有很多看起来很相似,但是用途却完全不同的语言要素,这些内容往往容易成为面试官考察你知识掌握程度的切入点。
灬沙师弟
2022/09/07
7500
Java-IO 流的Close方法
GC运行的时间点是不确定的(因为是一条单独存在的线程),所以很多时候你不能直接控制什么时候发生GC。这个带来的问题有两点,一个是有时候你的内存不足需要立刻回收而GC并不会立刻运行;另外一个是因为GC运行期间会占用大量系统资源所以某些情况下你会希望把它推后,或者干脆关掉以便根据性能需求在合式的时候手动执行。
Fisherman渔夫
2020/02/17
3.7K0
Java 中的 `try-catch-finally` 与 `try-with-resource`
https://blog.csdn.net/qq_29689343/article/details/95736854
訾博ZiBo
2025/01/06
940
基础篇:JAVA资源之IO、字节编码、URL和Spring.Resource
从源码角度理解Java设计模式——装饰者模式: https://www.cnblogs.com/thatme/p/10246837.html
潜行前行
2020/12/11
5790
基础篇:JAVA资源之IO、字节编码、URL和Spring.Resource
与面试官聊try-catch-finally关闭资源,你的答案还是10年前的?
有编程经验的朋友都知道,在程序运行中如果打开了一些资源,那么当发生异常或程序结束时都需要进行资源的关闭,不然会造成内存溢出的问题。
程序新视界
2021/12/07
6780
你有没有掉进去过这些 Exception 的“陷阱“(Part C)
在test包中新增测试类ConcurrentModificationExceptionTest
RiemannHypothesis
2022/08/19
2980
你有没有掉进去过这些 Exception 的“陷阱“(Part C)
你会使用try-with-resources吗
比如 InputStream、OutputStream,数据库连接对象 Connection,MyBatis中的 SqlSession 会话等。作为开发人员经常会忽略掉资源的关闭方法,导致内存泄漏。
cxuan
2019/09/11
1.4K0
Java异常面试题(2020最新版)
Java异常架构与异常关键字Java异常简介Java异常架构1. Throwable2. Error(错误)3. Exception(异常)运行时异常编译时异常
用户7886150
2020/12/18
7970
如何使用 try-with-resources 代替try-catch-finally?
try-with-resources 是 Java 7 引入的一种语法结构,用于自动关闭实现了 AutoCloseable 接口的资源。它可以代替传统的 try-catch-finally 结构来处理资源的释放。
程序员朱永胜
2023/10/10
2.1K0
哇塞!try-with-resouces 真神奇!
“二哥,终于等到你讲 try-with-resouces 了!”三妹夸张的表情让我有些吃惊。
沉默王二
2021/07/16
3590
java面试强基(11)
注意:不要在 finally 语句块中使用 return! 当 try 语句和 finally 语句中都有 return 语句时,try 语句块中的 return 语句会被忽略。这是因为 try 语句中的 return 返回值会先被暂存在一个本地变量中,当执行到 finally 语句中的 return 之后,这个本地变量的值就变为了 finally 语句中的 return 返回值。
一个风轻云淡
2023/10/15
1620
java面试强基(11)
Java异常处理流程
在Java应用中,异常的处理机制分为抛出异常和捕获异常。文章目录1.抛出异常2.捕获异常3.异Java
Java架构师必看
2021/07/15
9130
Java异常处理流程
浅谈 Java 中的 AutoCloseable 接口
本文对 try-with-resources 语法进行了较为深入的剖析,验证了其为一种语法糖,同时给出了其实际的实现方式的反编译结果,相信你在看完本文后,关于 AutoCloseable 的使用你会有新的收获。
2020labs小助手
2020/11/02
2K0
相关推荐
final 、finally、finalize有什么区别
更多 >
领券
社区富文本编辑器全新改版!诚邀体验~
全新交互,全新视觉,新增快捷键、悬浮工具栏、高亮块等功能并同时优化现有功能,全面提升创作效率和体验
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
查看详情【社区公告】 技术创作特训营有奖征文