Java高效编程(8):避免使用终结器和清理器

解锁Python编程的无限可能:《奇妙的Python》带你漫游代码世界

终结器(finalizers)是不可预测的,通常危险且不必要的。使用它们可能导致程序不稳定的行为、性能下降以及跨平台的兼容性问题。虽然终结器在某些情况下确实有用,但一般应尽量避免使用。自Java 9以来,终结器已被标记为废弃,取而代之的是清理器(cleaners)。虽然清理器相对更安全,但它们依然不可靠,且性能较差。因此,清理器也应当谨慎使用。

终结器与清理器的局限性

C++程序员可能会将终结器和清理器误解为Java的析构函数(destructor),但这种理解是不正确的。在C++中,析构函数是释放对象相关资源的常规方式,构成了与构造函数对称的操作。而在Java中,垃圾回收器会自动处理对象的内存回收,程序员不需要像在C++中那样手动释放内存资源。对于Java中的非内存资源(如文件、网络连接等),通常使用 try-with-resourcestry-finally 语句来管理资源的生命周期(详见【条目9】)。

终结器和清理器的主要问题之一在于,它们的执行时间不可预测。对象变为不可达到终结器或清理器执行之间的时间可能是任意长的。这意味着不应该将任何时间敏感的操作放在终结器或清理器中。比如,依赖终结器或清理器关闭文件是一种严重错误,因为文件描述符是有限的资源。如果系统无法及时回收这些资源,程序可能会因为无法再打开文件而崩溃。

终结器和清理器的执行依赖于垃圾回收器的调度策略,而不同的JVM实现可能使用不同的垃圾回收算法,这使得依赖终结器或清理器执行时间的程序行为难以保证一致性。例如,程序可能在开发环境中运行良好,但在生产环境中,由于垃圾回收器的行为不同,可能表现出完全不同的结果。

终结器的具体问题

终结器的另一个问题是,它们可能延迟对象的回收。为类提供终结器会导致实例的回收被任意延迟。我的一位同事曾遇到一个长期运行的GUI应用程序,在运行过程中频繁抛出 OutOfMemoryError。经过分析,发现当程序崩溃时,成千上万的图形对象正在终结器队列中等待回收,导致内存耗尽。更糟糕的是,终结器线程的优先级较低,导致终结器执行速度远低于对象生成的速度。语言规范并不保证终结器由哪个线程执行,因此无可移植的方式可以防止此类问题。相比之下,清理器稍微好一些,因为类的作者可以控制清理器的线程,但清理器依然是在后台运行,无法保证清理的及时性。

更严重的是,Java语言规范没有保证终结器或清理器会在对象不可达时一定被执行,甚至可能永远不会执行。因此,依赖终结器或清理器来更新持久化状态是非常危险的。例如,依赖终结器或清理器来释放共享资源(如数据库的锁)可能导致整个分布式系统停滞。

性能与安全问题

使用终结器或清理器还会带来严重的性能损失。在我的机器上,创建一个简单的 AutoCloseable 对象并通过 try-with-resources 关闭它的耗时约为12纳秒。而使用终结器处理对象则耗时550纳秒,足足慢了50倍。这是因为终结器会阻碍垃圾回收的高效运行。清理器的性能与终结器相当,但如果仅作为安全网使用,清理器的开销会稍小一些。在这种情况下,创建、清理和销毁一个对象的时间约为66纳秒,虽然依然比不使用清理器要慢5倍,但相比终结器要快得多。

除此之外,终结器还会带来一个潜在的安全问题——终结器攻击。攻击者可以通过抛出异常使构造函数或反序列化方法(如 readObjectreadResolve)未能正常结束,从而在对象应被销毁时调用终结器,恢复该对象并执行恶意代码。如果一个类是 final 的,那么它免疫终结器攻击;但对于非 final 类,防止此类攻击的方式是定义一个 final finalize 方法,且该方法不做任何操作。

使用AutoCloseable替代终结器

那么,当一个类封装了需要显式释放的资源(如文件、线程)时,应该怎么做呢?最好的解决方案是实现 AutoCloseable 接口,并要求用户在不再需要该对象时调用 close 方法。通常,用户可以使用 try-with-resources 来保证资源的释放,即使在异常情况下也能正常执行 close。此外,close 方法应记录对象已关闭的状态,确保在对象关闭后调用其他方法时抛出 IllegalStateException 异常。

清理器的作用与示例

尽管如此,清理器并非完全无用。它们的主要作用有两种:一是作为安全网,防止客户端忘记调用 close 方法。虽然清理器的执行时间不确定,但如果客户端未能关闭资源,迟到的清理总比永不清理要好。Java库中的一些类(如 FileInputStreamThreadPoolExecutor)就使用了终结器作为这种安全网。

二是用于处理本地对象(native peers)。本地对象是通过Java调用本地方法关联的非Java对象。因为垃圾回收器无法感知本地对象的存在,因此无法自动回收它们。此时,清理器可以用于释放这些本地资源。

下面是一个简单的 Room 类示例,演示了如何使用清理器作为安全网。假设房间在回收之前需要清理:

// 使用清理器的可自动关闭类
public class Room implements AutoCloseable {private static final Cleaner cleaner = Cleaner.create();private static class State implements Runnable {int numJunkPiles;State(int numJunkPiles) {this.numJunkPiles = numJunkPiles;}@Overridepublic void run() {System.out.println("清理房间");numJunkPiles = 0;}}private final State state;private final Cleaner.Cleanable cleanable;public Room(int numJunkPiles) {state = new State(numJunkPiles);cleanable = cleaner.register(this, state);}@Overridepublic void close() {cleanable.clean();}
}

在此示例中,Room 实现了 AutoCloseable,并使用了清理器作为安全网。如果用户未调用 close 方法,清理器会在房间对象被垃圾回收时自动执行清理工作。不过,如果用户在 try-with-resources 中使用 Room,那么清理器永远不会被触发。

// 良好使用的示例
public class Adult {public static void main(String[] args) {try (Room myRoom = new Room(7)) {System.out.println("Goodbye");}}
}

运行该程序后,输出为:

Goodbye
Cleaning room

但如果程序没有显式调用 close 方法,清理器的行为就不确定了。例如:

// 错误使用的示例
public class Teenager {public static void main(String[] args) {new Room(99);System.out.println("Peace out");}
}

在这段代码中,由于未使用 try-with-resourcesclose 方法,清理器可能永远不会运行。输出可能只是:

Peace out

要使清理器在程序结束时运行,可能需要调用 System.gc(),但即便如此,清理器的执行仍然依赖于具体的JVM实现,无法保证跨平台的一致性。

总结

终结器和清理器不应当作为资源管理的常规手段使用,除非作为安全网或用于释放非关键的本地资源。即便如此,也要警惕其不可预测性和性能开销。对于需要显式管理资源的类,推荐使用 AutoCloseable 接口和 try-with-resources 语句,这样可以确保资源被及时释放,避免不必要的资源泄露和安全问题。


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.xdnf.cn/news/1552092.html

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

D21【python接口自动化学习】-python基础之内置数据类型

day21 内置数据类型文档使用 学习日期:20240928 学习目标:内置数据类型--30 内置数据类型参考:如何使用官方文档与帮助? 学习笔记: 使用官方文档 可通过终端查看数据类型的方法 使用帮助 总结 官方文档是体系化的…

<使用生成式AI对四种冒泡排序实现形式分析解释的探讨整理>

<使用生成式AI对四种冒泡排序实现形式分析解释的探讨整理> 文章目录 <使用生成式AI对四种冒泡排序实现形式分析解释的探讨整理>1.冒泡排序实现形式总结1.1关于冒泡排序实现形式1的来源&#xff1a;1.2对四种排序实现形式使用AI进行无引导分析&#xff1a;1.3AI&…

【floor报错注入】

一、sql语句基础 floor 向下取整 count 取数据的数量 group by 分组查询 Rand 随机数 limit 二、floor报错注入 主键重复报错 我们先了解group by产生的虚拟表的原理&#xff0c;了解到虚拟表的主键是不可以重复的 我们再可以通过Rand(0)函数规定固定种子后乘2&…

Win10之Ubuntu22.04(主机)与Virtual-BOX(宿主win10)网络互通调试(七十九)

简介&#xff1a; CSDN博客专家、《Android系统多媒体进阶实战》一书作者 新书发布&#xff1a;《Android系统多媒体进阶实战》&#x1f680; 优质专栏&#xff1a; Audio工程师进阶系列【原创干货持续更新中……】&#x1f680; 优质专栏&#xff1a; 多媒体系统工程师系列【…

An End-to-End Local Attention Based Model for Table Recognition(ICDAR 2023)

An End-to-End Local Attention Based Model for Table Recognition(ICDAR 2023) 一.前述 作者认为基于Transformer的表格识别模型很难处理大表格的识别&#xff0c;原因是受限于它的全局注意力global attention机制。 基于以上&#xff0c;作者提出了一种局部注意力local a…

.NET Core 高性能并发编程

一、高性能大并发架构设计 .NET Core 是一个高性能、可扩展的开发框架&#xff0c;可以用于构建各种类型的应用程序&#xff0c;包括高性能大并发应用程序。为了设计和开发高性能大并发 .NET Core 应用程序&#xff0c;需要考虑以下几个方面&#xff1a; 1. 异步编程 异步编程…

在线css像素Px到百分比(%)换算器

具体请前往&#xff1a;在线Px转百分比(%)工具--将绝对像素(px)长度单位转换为相对父级元素内尺寸的相对长度单位百分比(%)

PCL 点云模型滤波(圆形)

目录 一、概述 1.1原理 1.2实现步骤 1.3应用场景 二、代码实现 2.1关键函数 2.1.1 生成点云数据 2.1.2 模型滤波函数 2.1.3 可视化函数 2.2完整代码 三、实现效果 PCL点云算法汇总及实战案例汇总的目录地址链接&#xff1a; PCL点云算法与项目实战案例汇总&#xf…

树和二叉树知识点大全及相关题目练习【数据结构】

树和二叉树 要注意树和二叉树是两个完全不同的结构、概念&#xff0c;它们之间不存在包含之类的关系 树的定义 树&#xff08;Tree&#xff09;是n&#xff08;n≥0&#xff09;个结点的有限集&#xff0c;它或为空树&#xff08;n 0&#xff09;&#xff1b;或为非空树&a…

lambda表达式底层实现:反编译LambdaMetafactory + 转储dump + 运行过程 + 反汇编 + 动态指令invokedynamic

一、结论先行 lambda 底层实现机制 1.lambda 表达式的本质&#xff1a;函数式接口的匿名子类的匿名对象 2.lambda表达式是语法糖 语法糖&#xff1a;编码时是lambda简洁的表达式&#xff0c;在字节码期&#xff0c;语法糖会被转换为实际复杂的实现方式&#xff0c;含义不变&am…

基于springboot的数据库原理教学案例案例库管理系统

目录 毕设制作流程功能和技术介绍系统实现截图开发核心技术介绍&#xff1a;使用说明开发步骤编译运行代码执行流程核心代码部分展示可行性分析软件测试详细视频演示源码获取 毕设制作流程 &#xff08;1&#xff09;与指导老师确定系统主要功能&#xff1b; &#xff08;2&am…

PMP--三模--解题--71-80

文章目录 7.成本管理--S曲线--S曲线对累计值进行监督和报告--S曲线可以同时报告成本与进度情况。适用于预测和敏捷项目。14.敏捷--信息发射源--是一种可见的实物展示其向组织内其他成员提供信息在不干扰团队的情况下即时实现知识共享。71、 [单选] 项目经理正在为刚刚进入第三次…

windows配置C++编译环境和VScode C++配置(保姆级教程)

1.安装MinGW-w64 MinGW-w64是一个开源的编译器套件,适用于Windows平台,支持32位和64位应用程序的开发。它包含了GCC编译器、GDB调试器以及其他必要的工具,是C++开发者在Windows环境下进行开发的重要工具。 我找到了一个下载比较快的链接:https://gitcode.com/open-source-…

FastAPI 第九课 -- 表单数据

目录 一. 前言 二. 声明表单数据模型 三. 在路由中接收表单数据 四. 表单数据的验证和文档生成 五. 处理文件上传 一. 前言 在 FastAPI 中&#xff0c;接收表单数据是一种常见的操作&#xff0c;通常用于处理用户通过 HTML 表单提交的数据。 FastAPI 提供了 Form 类型&a…

C++发邮件:如何轻松实现邮件自动化发送?

C发邮件的详细步骤与教程指南&#xff1f;如何在C中发邮件&#xff1f; 无论是定期发送报告、通知客户还是管理内部沟通&#xff0c;自动化邮件系统都能显著提升工作效率。AokSend将详细介绍如何使用C发邮件&#xff0c;实现邮件自动化发送&#xff0c;帮助您轻松管理邮件通信…

车视界系统小程序的设计

管理员账户功能包括&#xff1a;系统首页&#xff0c;个人中心&#xff0c;汽车品牌管理&#xff0c;汽车颜色管理&#xff0c;用户管理&#xff0c;汽车信息管理&#xff0c;汽车订单管理系统管理 微信端账号功能包括&#xff1a;系统首页&#xff0c;汽车信息&#xff0c;我…

4.1、FineReport单元格扩展和父子格

单元格扩展 1、配置数据集 2、纵向扩展 方法一&#xff1a; 方法二&#xff1a; 结果 多个字段纵向 2、横向扩展 方法一&#xff1a; 方法二&#xff1a; 结果 父子格 没什么特殊要求&#xff0c;就保持默认 1、右边的值默认以左边为左父格 2、下边的值默认以上边…

【Windows】如何取消显示Windows聚焦在桌面上生成的“了解此图片”图标

如下图所示&#xff0c;在更换Windows聚焦显示的时候&#xff0c;会在桌面多出一个“了解此图片”的图标&#xff0c;看着很烦&#xff0c;但又因为Windows聚焦自带的壁纸比其他主题的壁纸好看很多。 下面是消除办法&#xff1a; 打开注册表&#xff08;按WindowsR&#xff0…

【COSMO-SkyMed系列的4颗卫星主要用途】

COSMO-SkyMed系列的4颗卫星主要用于提供一个多用途的对地观测平台&#xff0c;服务于民间、公共机构、军事和商业领域。以下是这4颗卫星的主要用途&#xff1a; 民防与环境风险管理&#xff1a; 卫星的高分辨率雷达图像可用于监测自然灾害&#xff0c;如地震、洪水、滑坡等&am…

51单片机学习第六课---B站UP主江协科技

DS18B20 1、基本知识讲解 2、DS18B20读取温度值 main.c #include<regx52.h> #include"delay.h" #include"LCD1602.h" #include"key.h" #include"DS18B20.h"float T; void main () {LCD_Init();LCD_ShowString(1,1,"temp…