科普文:一文搞懂jvm实战(二)Cleaner回收jvm资源

news2024/11/18 12:36:54

概叙

        在JDK9中新增了Cleaner类,该类的作用是用于替代finalize方法,更有效地释放资源并避免内存泄漏。

        在JEP260提案中,封装了大部分Sun包内部的API之余,还引入了一些新的API,其中就包含着Cleaner这个工具类。Cleaner承担着替换finalize方法的作用,为了解决finalize方法的性能问题、安全问题以及不可靠。

        在JDK1.2中,就已经有Cleaner这个类的内部实现了,不过是在sun包中实现的。由于是内部类不建议在生产代码中直接使用。不过sun包下的Cleaner类和lang包下的Cleanr类的功能是类似的。

        在Java中,Cleanerjava.lang.ref.Cleaner类的一个实例,它用于在垃圾收集器确定对象不再可达时执行清理动作。通常,这在处理本地资源(如文件句柄或数据库连接)时非常有用,确保资源在对象不再使用时能够得到释放。

        使用Cleaner的典型场景是结合使用java.lang.ref.PhantomReferencejava.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("诞生!");
   }
   @Override
	protected 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 对象

  1. 使用Cleaner.create()创建Cleaner对象。
  2. 调用cleaner.register()方法,传入监听的对象以及回收后要执行的逻辑。其中,逻辑中不能带有监听对象的引用,否则对象将永远无法被回收。

Cleaner 优点和局限性


Cleaner 类相比 Finalizer 和 PhantomReference 的优势


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


Cleaner 类的潜在问题和限制

  1. 每注册一个Cleaner类,就会新开一条线程用于监听目标对象是否已经进入到引用队列。直到目标对象被回收后,新线程才结束。
  2. Cleaner回收时间点无法控制。
  3. 不能替换所有的资源释放,必要时还是需要显式执行Close方法。
  4. 无法控制传入的回收执行逻辑,可能导致性能问题。

应用场景

        在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("【构造】用心编写了一本优秀的原创技术图书!");
    }

    @Override
    protected 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("【构造】用心编写了一本优秀的原创技术图书!");
    }

    @Override
    protected 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("【读书】认真学习!");
    }
    @Override
    public 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);//注册一个回收线程
    }

    @Override
    public 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("诞生!");
	}
	@Override
	public 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) ;	// 注册使用的对象
	}
	@Override
	public 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中对象的生命周期流程

  1. 创建阶段:每当使用关键字new就表示要开辟新的堆内存空间,同时每一个新的对象实例化时都需要去执行类中的构造方法,构造方法的目的是为了类中成员属性的初始化
  2. 应用阶段:利用指定的对象名称可以直接进行类之中的方法的调用处理
  3. 不可见阶段:如果现实某一个方法内部有一个对象,则该方法执行完毕后该对象将不再使用
  4. 不可达阶段:某一块堆内存已经不再有任何的栈内存所指向,那么这块空间将成为垃圾空间
  5. 收集阶段:JVM会自动的进行此块垃圾空间的标记,标记之后将准备通过GC回收释放,JDK1.8及以前的版本均使用finalize()方法,JKD1.9及以后的版本推荐使用CLeaner来完成
  6. 释放阶段:JVM重新回收垃圾的堆内存空间,供后续新对象使用

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

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

相关文章

JAVA程序打包时报错,但是运行时正常。

报错:Could not transfer artifact com.alibaba:fastjson:pom:1.2.83 from/to clojars... 背景:需要将fastjson从1.2.70升级到1.2.83;并且编译环境是局域网不可以连接互联网;每个项目组都是独立的私有仓库。 操作:在本…

品牌推广的深层逻辑:自我提升与市场认同的和谐共生

品牌推广的深层逻辑:自我提升与市场认同的和谐共生 著名飞行员查尔斯林德伯格(Charles Lindbergh) 曾写道:“改善生活方式比传播生活方式更重要。如果我们自己的生活方式使别人感到满意,那么它将自动蔓延。如果不是这样,那么任何武力都不可能…

kotlin协程的理解

伴生对象:companion object 其实质等同于Java中的单例模式 协程:通常实现是用户态的任务协作式调度 一段可执行代码可挂起/可恢复执行概念上与语言无关,协程这个概念于1958年提出 依赖框架: 协程的启动: 1.协程体&a…

【SVN的使用-源代码管理工具-命令行的使用 Objective-C语言】

一、接下来,我们来说一个终端的命令行的使用, 1.我们说,你的电脑里边呢,有终端, 在Mac里边,你想新建一个txt,应该怎么写,对,打开文本编辑, 打开这个东西,写点儿东西,然后保存一下,保存的时候,你还要去选择格式, 现在,如果我们用命令行,可以更方便一些, 2.首…

SCT612404通道,高效高集成,摄像头模组电源集成芯片

集成三路降压变换器,1CH高压BUCK,2CH低压Buck >HVBuck1:输入电压4.0V-20V,输出电流1.2A,Voo300mV/500mV >LVBuck2:输入电压2.7V-5V,输出电流0.6A , 固定1.8V输出 ;LVBuck3:输λ2.7V-5V,输出电流1.2A,可设定固定输出: 1 . 1 V / 1 . 2 V / 1 . 3 …

vite项目配置svg图标(vite-plugin-svg-icons)

1.插件地址 网址 , 可以去里面查看中文文档,里面有详情的教程 2.使用, 如果你安装的有element-plus ,可以使用这样的方式来修改大小和颜色 <el-icon size"18" color"red"><SvgIcon name"xing"></SvgIcon></el-icon> …

MViT(ICCV 2021, Meta)论文解读

paper&#xff1a;Multiscale Vision Transformers official implementation&#xff1a;https://github.com/facebookresearch/SlowFast 背景和出发点 这篇文章提出了多尺度视觉Transformer&#xff08;Multiscale Vision Transformers, MViT&#xff09;的概念&#xff0c…

nftables(1)基本原理

简介 nftables 是 Linux 内核中用于数据包分类的现代框架&#xff0c;用来替代旧的 iptables&#xff08;包括 ip6tables, arptables, ebtables 等&#xff0c;统称为 xtables&#xff09;架构。nftables 提供了更强大、更灵活以及更易于管理的规则集配置方式&#xff0c;使得…

中国1km高分辨率高质量逐年近地表CO数据集(2013-2022年)

该数据为中国高分辨率高质量逐年CO数据集&#xff0c;该数据集主要的空间范围覆盖整个中国&#xff0c;其中内容包括中国1km高分辨率高质量逐年CO数据集(2013-2022年)。时间分辨率为年&#xff0c;单位为mg/m3&#xff0c;数据以(.nc/.tif)格式进行存储。

Vscode快捷键崩溃

Vscode快捷键崩溃 Linux虚拟机下使用vscode写代码【ctrlA&#xff0c;CtrlC&#xff0c;CtrlV】等快捷键都不能使用&#xff0c;还会出现“NO text insert“等抽象的指令&#xff0c;问题就是不知道什么时候装了一个VIM插件&#xff0c;让他滚出电脑》》》

快速傅里叶变换(Fast Fourier Transform)

快速算法&#xff08;FFT&#xff09;&#xff0c;即快速傅里叶变换&#xff08;Fast Fourier Transform&#xff09;&#xff0c;是一种用于计算离散傅里叶变换&#xff08;DFT&#xff09;及其逆变换的高效算法。FFT算法由J.W.库利和T.W.图基于1965年提出&#xff0c;显著减少…

T100-XG查询报表的开发

制作XG报表 1、注册程序 azzi900 首先现将程序注册一下,在内部构建基础代码档。 2、注册作业 azzi910 也是直接新增一个,作业跟程序绑定一下。 3、T100签出规格程序 这个时候应该是没签出的,首先将规格迁出。 4、T100画面产生器 规格迁出之后,这个时候还需要生成一个画…

【探索Linux】P.37(传输层 —— TCP协议通信机制 | 确认应答(ACK)机制 | 超时重传机制)

阅读导航 引言一、确认应答(ACK)机制1. 成功接收2. 过程中存在丢包3. 引入序列号&#xff08;1&#xff09;序列号的定义&#xff08;2&#xff09;序列号的作用&#xff08;3&#xff09;序列号的工作原理&#xff08;4&#xff09;序列号和确认应答号 二、超时重传机制1. 超时…

Linux/Ubuntu访问局域网共享文件夹

文件夹中找到“Other Location”&#xff0c;输入“smb:IP地址/共享文件夹名称”&#xff0c;然后点击connect后者直接回车即可&#xff01; End&#xff01;

【毛发教程】使用 Maya、XGen 和虚幻引擎创建马尾辫发型

Malte Resenberger-Loosmann是国外一名首席艺术家&#xff0c;他负责指导整个艺术部门来制作独立游戏项目中的3D建模。在本文中&#xff0c;Loosmann展示了马尾辫发型背后的工作流程&#xff0c;分享了 Maya 和虚幻引擎中的场景设置&#xff0c;并解释了 GS CurveTools 如何帮助…

RTL8211FSI PHY电路设计

文章目录 硬件设计引脚功能框图说明PHYADDRPageLED 模式自动协商/速度/全半双工模式Soft Reset上电顺序 原理图设计参考 软件控制&#xff08;FPGA&#xff09;硬件调试 硬件设计 引脚 笔者前代数字采集板采用的 PHY 芯片是博通 Boardcom 的 B50610&#xff0c;其仅支持 0 ∼…

从零到一:eBay自养号测评全流程解析与实操建议

eBay自养号测评是一种通过模拟真实买家行为&#xff0c;为卖家提供市场反馈并提升店铺权重和排名的技术手段。以下是进行eBay自养号测评的具体步骤和注意事项&#xff1a; 一、准备阶段 1. 技术配置&#xff1a;搭建境外服务器&#xff1a;选择稳定的境外服务器&#xff0c;模…

【解码现代 C++】:实现自己的智能 【String 类】

目录 1. 经典的String类问题 1.1 构造函数 小李的理解 1.2 析构函数 小李的理解 1.3 测试函数 小李的理解 1.4 需要记住的知识点 2. 浅拷贝 2.1 什么是浅拷贝 小李的理解 2.2 需要记住的知识点 3. 深拷贝 3.1 传统版写法的String类 3.1.1 拷贝构造函数 小李的理…

go zero入门

一、goctl安装 goctl 是 go-zero 的内置脚手架&#xff0c;可以一键生成代码、文档、部署 k8s yaml、dockerfile 等。 # Go 1.16 及以后版本 go install github.com/zeromicro/go-zero/tools/goctllatest检查是否安装成功 $ goctl -v goctl version 1.6.6 darwin/amd64vscod…

0/1背包问题总结

文章目录 &#x1f347;什么是0/1背包问题&#xff1f;&#x1f348;例题&#x1f349;1.分割等和子集&#x1f349;2.目标和&#x1f349;3.最后一块石头的重量Ⅱ &#x1f34a;总结 博客主页&#xff1a;lyyyyrics &#x1f347;什么是0/1背包问题&#xff1f; 0/1背包问题是…