科普文:一文搞懂jvm实战(二)Cleaner回收jvm资源
概叙
在JDK9中新增了Cleaner类,该类的作用是用于替代finalize方法,更有效地释放资源并避免内存泄漏。
在JEP260提案中,封装了大部分Sun包内部的API之余,还引入了一些新的API,其中就包含着Cleaner这个工具类。Cleaner承担着替换finalize方法的作用,为了解决finalize方法的性能问题、安全问题以及不可靠。
在JDK1.2中,就已经有Cleaner这个类的内部实现了,不过是在sun包中实现的。由于是内部类不建议在生产代码中直接使用。不过sun包下的Cleaner类和lang包下的Cleanr类的功能是类似的。
在Java中,Cleaner是java.lang.ref.Cleaner类的一个实例,它用于在垃圾收集器确定对象不再可达时执行清理动作。通常,这在处理本地资源(如文件句柄或数据库连接)时非常有用,确保资源在对象不再使用时能够得到释放。
使用Cleaner的典型场景是结合使用java.lang.ref.PhantomReference和java.lang.ref.ReferenceQueue。你可以创建一个PhantomReference到你想要跟踪的对象,并在创建时指定一个ReferenceQueue和一个Cleaner。当PhantomReference被入队时,Cleaner会执行一个清理方法,你可以在这个方法中释放资源。
Cleaner类
注意:在JDK1.9以上版本可使用
在Java程序中提供有GC的垃圾回收机制,如果发现堆内存不足时一定要进行垃圾回收以释放内存空间,但如果某些对象在回收前需要做一些处理,可以通过覆写Object类中的finalize()方法来实现这种回收前的处理。
-
finalize()方法的定义:
@Deprecated(since="9")
protected void finalize() throws Throwable { }
finalize()方法实际上从JDK1.0时就开始提供,但一直到JDK1.9后才发现此方法成为了不推荐使用的方法,同时这个方法上所抛出的一个Throwabke类型的异常(Erroe和Exception),在对象回收时可能会出现各种问题,但不影响回收!
public class Member {public Member() {System.out.println("诞生!");}@Overrideprotected void finalize() throws Throwable { //可能抛错误,也可能抛异常System.out.println("回收!");throw new Exception("我真的还想在活500年");}
}public class Demo {public static void main(String[] args) {Member mem=new Member(); //实例化对象mem=null; //垃圾,不被引用System.gc();//手动进行gc操作System.out.println("太阳照常升起,一代更比一代强");}
}执行结果:诞生!
太阳照常升起,一代更比一代强
回收回收对象前要先finalize(),降低了内存回收的效率,而且它不能保证被及时执行,或者可能造成该对象的在次复活。
jdk1.9后出现新的替代者:java.lang.ref.Cleaner类。此种清理方式会启动一个新的清理线程,并且基于AutoCloseable接口实现资源释放。
Cleaner 类的主要方法和属性
Cleaner类中,一共就三个外部方法,一个简单的工具类。

其中,在调用create方法时,就会新建一条线程,用于监听目标对象是否已经被回收。监听的逻辑则是用到了虚引用以及引用队列,在虚引用中,要是一个对象变成不可达后,在GC前会将该对象的虚引用放入引用队列中。详细的步骤以及逻辑可以看这篇文章【Java引用规范】虚引用以及引用队列。
如何创建和使用 Cleaner 对象
- 使用
Cleaner.create()创建Cleaner对象。 - 调用
cleaner.register()方法,传入监听的对象以及回收后要执行的逻辑。其中,逻辑中不能带有监听对象的引用,否则对象将永远无法被回收。
Cleaner 优点和局限性
Cleaner 类相比 Finalizer 和 PhantomReference 的优势

Cleaner 类和手动调用Close方法的区别

Cleaner 类的潜在问题和限制
- 每注册一个Cleaner类,就会新开一条线程用于监听目标对象是否已经进入到引用队列。直到目标对象被回收后,新线程才结束。
- Cleaner回收时间点无法控制。
- 不能替换所有的资源释放,必要时还是需要显式执行Close方法。
- 无法控制传入的回收执行逻辑,可能导致性能问题。
应用场景
在JDK1.2中,就已经有这个类的内部实现了,不过是在sun包中实现的。由于是内部类不建议在生产代码中直接使用。不过sun包下的Cleaner类和lang包下的Cleanr类的功能是类似的。
Cleaner在JDK中最典型的实现就是堆外内存的回收。我们申请到一个堆外内存后,是无法手动将该堆外内存进行显示的回收的,只能等待JVM来自动回收该内存。
其中,自动回收的操作就是使用到了Cleaner工具类,在DirectByteBuffer的构造方法中,申请到堆外内存后,就会将堆外内存地址、申请容量以及实际内存大小传入到Deallocator类中进行空间的回收。

Deallocator类集成了Runnable接口,在run方法中就会将对应地址的堆外内存回收。

示例1:传统的对象回收
- li不进行手动回收
class Book{//无参构造public Book(){System.out.println("【构造】用心编写了一本优秀的原创技术图书!");}@Overrideprotected void finalize() throws Throwable {System.out.println("【析构】图书使用完毕,可以销毁!");}
}
public class Application {public static void main(String[] args) {Book book = new Book();//创建实例化对象book = null;//断开堆内存的指向,变为垃圾空间}
}运行结果:【构造】用心编写了一本优秀的原创技术图书!
- 调用gc()进行手动回收
如果不进行手动的gc()回收,则需要等待自动回收,自动回收的时间是不可控的!!
class Book{//无参构造public Book(){System.out.println("【构造】用心编写了一本优秀的原创技术图书!");}@Overrideprotected void finalize() throws Throwable {System.out.println("【析构】图书使用完毕,可以销毁!");}
}
public class Application {public static void main(String[] args) {Book book = new Book();//创建实例化对象book = null;//断开堆内存的指向,变为垃圾空间//如果不进行手动的gc()回收,则需要等待自动回收,自动回收的时间是不可控的!!System.gc();//进行垃圾回收}
}运行结果如下:【构造】用心编写了一本优秀的原创技术图书!
【析构】图书使用完毕,可以销毁!
- 手动抛出异常
finalize()方法代码修改如下:(其他代码不变)
protected void finalize() throws Throwable {System.out.println("【析构】图书使用完毕,可以销毁!");throw new Exception("这本图书还有用,不能销毁!!");//手动抛出异常}运行结果如下:【构造】用心编写了一本优秀的原创技术图书!
【析构】图书使用完毕,可以销毁!
以上的这种程序做法是在JDK1.9以前提供的处理形式,但这样的做法一直以来都存在严重问题。
如果在finalize()里面出现一些线程的死锁操作,那么就可能会造成垃圾回收的失败,同时也会产生严重的线程阻塞问题
解决方法:在JDK1.9之后,启动了一个专属的回收线程----Cleaner类
示例2:cleanable.clean();//释放时进行垃圾清除
import sun.misc.Cleaner;
class Book implements Runnable{ //设计一个回收线程//无参构造public Book(){System.out.println("【构造】用心编写了一本优秀的原创技术图书!");}public void read(){System.out.println("【读书】认真学习!");}@Overridepublic void run() { //真正的回收由线程来完成!!!System.out.println("【析构】图书使用完毕,可以销毁!");}
}
class BookCleaner implements AutoCloseable{ //必须实现AutoCloseable接口private static final Cleaner cleaner = Cleaner.create();//创建一个回收对象private Cleaner.Cleanable cleanable;public BookCleaner(Book book){this.cleanable = cleaner.register(this,book);//注册一个回收线程}@Overridepublic void close() throws Exception {this.cleanable.clean();//释放时进行垃圾清除}
}
public class Application {public static void main(String[] args) {Book book = new Book();try(BookCleaner bc = new BookCleaner()){book.read();//可以在中间进行一些对象的处理操作}catch (Exception e){}}
}运行结果如下:【构造】用心编写了一本优秀的原创技术图书!
【读书】认真学习!
【析构】图书使用完毕,可以销毁!

package cn.mldn.demo;
import java.lang.ref.Cleaner;
class Member implements Runnable {public Member() {System.out.println("诞生!");}@Overridepublic void run() { // 清除线程System.out.println("回收!");}
}
class MemberCleaning implements AutoCloseable { // 实现清除的处理private static final Cleaner cleaner = Cleaner.create() ; // 创建一个清除处理private Cleaner.Cleanable cleanable ;public MemberCleaning(Member member) { // 注册待清除对象this.cleanable = cleaner.register(this, member) ; // 注册使用的对象}@Overridepublic void close() throws Exception {// 自动关闭并释放this.cleanable.clean(); // 启动清理线程}
}
public class JavaAPIDemo {public static void main(String[] args) throws Exception {Member mem = new Member() ;// 实例化对象System.gc(); // 手工进行GC调用try (MemberCleaning mc = new MemberCleaning(mem)){/ 中间可以执行一些相关的代码} catch (Exception e) {}System.out.println("太阳照常升起,地球照样转动,一代更比一代强!");// 不受影响继续执行}
}执行结果诞生!
太阳照常升起,一代更比一代强
回收新版本要求启动一个线程单独清除回收,防止延迟处理,保证性能,但是也不能保证垃圾被及时回收。最好的方式是用完即使用try-with-resource机制显示释放或者放入资源池重用。
图解-java对象的生命周期
经过以上的分析基本已经清楚对象的创建以及回收处理的操作,以下是对Java中对象的生命周期流程

- 创建阶段:每当使用关键字new就表示要开辟新的堆内存空间,同时每一个新的对象实例化时都需要去执行类中的构造方法,构造方法的目的是为了类中成员属性的初始化
- 应用阶段:利用指定的对象名称可以直接进行类之中的方法的调用处理
- 不可见阶段:如果现实某一个方法内部有一个对象,则该方法执行完毕后该对象将不再使用
- 不可达阶段:某一块堆内存已经不再有任何的栈内存所指向,那么这块空间将成为垃圾空间
- 收集阶段:JVM会自动的进行此块垃圾空间的标记,标记之后将准备通过GC回收释放,JDK1.8及以前的版本均使用finalize()方法,JKD1.9及以后的版本推荐使用CLeaner来完成
- 释放阶段:JVM重新回收垃圾的堆内存空间,供后续新对象使用
相关文章:
科普文:一文搞懂jvm实战(二)Cleaner回收jvm资源
概叙 在JDK9中新增了Cleaner类,该类的作用是用于替代finalize方法,更有效地释放资源并避免内存泄漏。 在JEP260提案中,封装了大部分Sun包内部的API之余,还引入了一些新的API,其中就包含着Cleaner这个工具类。Cleaner承…...
使用PyTorch高效读取二进制数据集进行训练
使用pickle制作类cifar10二进制格式的数据集 使用pytorc框架来训练(以猫狗大战数据集为例) 此方法是为了实现阿里云PAI studio上可视化训练模型时使用的数据格式。 一、制作类cifar10二进制格式数据 import os, cv2 from pickled import * from load_da…...
应急响应:应急响应流程,常见应急事件及处置思路
「作者简介」:冬奥会网络安全中国代表队,CSDN Top100,就职奇安信多年,以实战工作为基础著作 《网络安全自学教程》,适合基础薄弱的同学系统化的学习网络安全,用最短的时间掌握最核心的技术。 这一章节我们需…...
Kotlin/Android中执行HTTP请求
如何在Kotlin/Android中执行简单的HTTP请求 okhttp官网 okhttp3 github地址 打开build.gradle.kts文件加入依赖 dependencies {implementation("com.squareup.okhttp3:okhttp:4.9.0") }在IDEA的Gradle面板点击reload按钮便会自动下载jar...
哈希表(C++实现)
文章目录 写在前面1. 哈希概念2. 哈希冲突3. 哈希函数4.哈希冲突解决4.1 闭散列4.1.1 线性探测4.1.2 采用线性探测的方式解决哈希冲突实现哈希表4.1.3 二次探测 4.2 开散列4.2.2 采用链地址法的方式解决哈希冲突实现哈希表 写在前面 在我们之前实现的所有数据结构中(比如&…...
深入理解代理模式(Proxy Pattern)及其实际应用
引言 在软件开发中,有时候我们需要在不改变现有代码的情况下添加一些功能,比如延迟初始化、访问控制、日志记录等。代理模式(Proxy Pattern)通过代理对象控制对原对象的访问,为现有代码添加了额外的功能。本篇文章将详…...
Elasticsearch (1):ES基本概念和原理简单介绍
Elasticsearch(简称 ES)是一款基于 Apache Lucene 的分布式搜索和分析引擎。随着业务的发展,系统中的数据量不断增长,传统的关系型数据库在处理大量模糊查询时效率低下。因此,ES 作为一种高效、灵活和可扩展的全文检索…...
【Python爬虫】Python爬取喜马拉雅,爬虫教程!
一、思路设计 (1)分析网页 在喜马拉雅主页找到自己想要的音频,得到目标URL:https://www.ximalaya.com/qinggan/321787/ 通过分析页面的网络抓包,最终的到一个比较有用的json数据包 通过分析,得到了发送json…...
基于Jmeter的分布式压测环境搭建及简单压测实践
写在前面 平时在使用Jmeter做压力测试的过程中,由于单机的并发能力有限,所以常常无法满足压力测试的需求。因此,Jmeter还提供了分布式的解决方案。本文是一次利用Jmeter分布式对业务系统登录接口做的压力测试的实践记录。按照惯例࿰…...
IDEA常用代码模板
在 IntelliJ IDEA 中,常用代码模板可以帮助你快速生成常用的代码结构和模式。以下是一些常用的代码模板及其使用方法: 动态模板(Live Templates) psvm:生成 public static void main(String[] args) 方法。sout:生成 System.out.println(); 语句。soutv:生成 System.ou…...
基于大语言模型的多意图增强搜索
随着人工智能技术的蓬勃发展,大语言模型(LLM)如Claude等在多个领域展现出了卓越的能力。如何利用这些模型的语义分析能力,优化传统业务系统中的搜索性能是个很好的研究方向。 在传统业务系统中,数据匹配和检索常常面临…...
【ai】ubuntu18.04 找不到 nvcc --version问题
nvcc --version显示command not found问题 这个是cuda 库: windows安装了12.5 : 参考大神:解决nvcc --version显示command not found问题 原文链接:https://blog.csdn.net/Flying_sfeng/article/details/103343813 /usr/local/cuda/lib64 与 /usr/local/cuda-11.3/lib64 完…...
深入了解DDoS攻击及其防护措施
深入了解DDoS攻击及其防护措施 分布式拒绝服务(Distributed Denial of Service,DDoS)攻击是当今互联网环境中最具破坏性和普遍性的网络威胁之一。DDoS攻击不仅危及企业的运营,还可能损害其声誉,造成客户信任度的下降。…...
【面试系列】产品经理高频面试题及详细解答
欢迎来到我的博客,很高兴能够在这里和您见面!欢迎订阅相关专栏: ⭐️ 全网最全IT互联网公司面试宝典:收集整理全网各大IT互联网公司技术、项目、HR面试真题. ⭐️ AIGC时代的创新与未来:详细讲解AIGC的概念、核心技术、…...
前端特殊字符数据,后端接收产生错乱,前后端都需要处理
前端: const data {createTime: "2024-06-11 09:58:59",id: "1800346960914579456",merchantId: "1793930010750218240",mode: "DEPOSIT",channelCode: "if(amount > 50){iugu2pay;} else if(amount < 10){iu…...
力扣热100 哈希
哈希 1. 两数之和49.字母异位词分组128.最长连续序列 1. 两数之和 题目:给定一个整数数组 nums 和一个整数目标值 target,请你在该数组中找出 和为目标值 target 的那 两个 整数,并返回它们的数组下标。你可以假设每种输入只会对应一个答案。…...
[图解]SysML和EA建模住宅安全系统-05-参数图
1 00:00:01,140 --> 00:00:03,060 这是实数没错,这是分钟 2 00:00:03,750 --> 00:00:07,490 但是你在这里选,选不了的 3 00:00:07,500 --> 00:00:09,930 因为它这里不能够有那个 4 00:00:11,990 --> 00:00:13,850 但是我们前面这里 5 00…...
JavaScript——对象的创建
目录 任务描述 相关知识 对象的定义 对象字面量 通过关键字new创建对象 通过工厂方法创建对象 使用构造函数创建对象 使用原型(prototype)创建对象 编程要求 任务描述 本关任务:创建你的第一个 JavaScript 对象。 相关知识 JavaScript 是一种基于对象&a…...
大二暑假 + 大三上
希望,暑假能早睡早起,胸围达到 95,腰围保持 72,大臂 36,小臂 32,小腿 38🍭🍭 目录 🍈暑假计划 🌹每周进度 🤣寒假每日进度😂 &…...
C语言使用先序遍历创建二叉树
#include<stdio.h> #include<stdlib.h>typedef struct node {int data;struct node * left;struct node * right; } Node;Node * createNode(int val); Node * createTree(); void freeTree(Node * node);void preOrder(Node * node);// 先序创建二叉树 int main()…...
React 第五十五节 Router 中 useAsyncError的使用详解
前言 useAsyncError 是 React Router v6.4 引入的一个钩子,用于处理异步操作(如数据加载)中的错误。下面我将详细解释其用途并提供代码示例。 一、useAsyncError 用途 处理异步错误:捕获在 loader 或 action 中发生的异步错误替…...
Cursor实现用excel数据填充word模版的方法
cursor主页:https://www.cursor.com/ 任务目标:把excel格式的数据里的单元格,按照某一个固定模版填充到word中 文章目录 注意事项逐步生成程序1. 确定格式2. 调试程序 注意事项 直接给一个excel文件和最终呈现的word文件的示例,…...
AtCoder 第409场初级竞赛 A~E题解
A Conflict 【题目链接】 原题链接:A - Conflict 【考点】 枚举 【题目大意】 找到是否有两人都想要的物品。 【解析】 遍历两端字符串,只有在同时为 o 时输出 Yes 并结束程序,否则输出 No。 【难度】 GESP三级 【代码参考】 #i…...
如何将联系人从 iPhone 转移到 Android
从 iPhone 换到 Android 手机时,你可能需要保留重要的数据,例如通讯录。好在,将通讯录从 iPhone 转移到 Android 手机非常简单,你可以从本文中学习 6 种可靠的方法,确保随时保持连接,不错过任何信息。 第 1…...
uniapp微信小程序视频实时流+pc端预览方案
方案类型技术实现是否免费优点缺点适用场景延迟范围开发复杂度WebSocket图片帧定时拍照Base64传输✅ 完全免费无需服务器 纯前端实现高延迟高流量 帧率极低个人demo测试 超低频监控500ms-2s⭐⭐RTMP推流TRTC/即构SDK推流❌ 付费方案 (部分有免费额度&#x…...
《基于Apache Flink的流处理》笔记
思维导图 1-3 章 4-7章 8-11 章 参考资料 源码: https://github.com/streaming-with-flink 博客 https://flink.apache.org/bloghttps://www.ververica.com/blog 聚会及会议 https://flink-forward.orghttps://www.meetup.com/topics/apache-flink https://n…...
【电力电子】基于STM32F103C8T6单片机双极性SPWM逆变(硬件篇)
本项目是基于 STM32F103C8T6 微控制器的 SPWM(正弦脉宽调制)电源模块,能够生成可调频率和幅值的正弦波交流电源输出。该项目适用于逆变器、UPS电源、变频器等应用场景。 供电电源 输入电压采集 上图为本设计的电源电路,图中 D1 为二极管, 其目的是防止正负极电源反接, …...
Caliper 负载(Workload)详细解析
Caliper 负载(Workload)详细解析 负载(Workload)是 Caliper 性能测试的核心部分,它定义了测试期间要执行的具体合约调用行为和交易模式。下面我将全面深入地讲解负载的各个方面。 一、负载模块基本结构 一个典型的负载模块(如 workload.js)包含以下基本结构: use strict;/…...
作为测试我们应该关注redis哪些方面
1、功能测试 数据结构操作:验证字符串、列表、哈希、集合和有序的基本操作是否正确 持久化:测试aof和aof持久化机制,确保数据在开启后正确恢复。 事务:检查事务的原子性和回滚机制。 发布订阅:确保消息正确传递。 2、性…...
【FTP】ftp文件传输会丢包吗?批量几百个文件传输,有一些文件没有传输完整,如何解决?
FTP(File Transfer Protocol)本身是一个基于 TCP 的协议,理论上不会丢包。但 FTP 文件传输过程中仍可能出现文件不完整、丢失或损坏的情况,主要原因包括: ✅ 一、FTP传输可能“丢包”或文件不完整的原因 原因描述网络…...
