如何判断对象是否是垃圾

news2024/11/27 4:21:27

前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。点击跳转到网站。

我们都Java会自动进行内存管理,JVM会进行垃圾回收,哪它是怎么判定哪些是“垃圾”并决定“垃圾”的生死呢?

判断对象是否为“垃圾”

Java有两种算法判断对象是否是垃圾:引用计数算法和可达性分析算法。

引用计数算法

引用计数(Reference Counting)算法就是给对象加一个引用计数器,当对象被引用,计数器加一;当引用失效时,计数器减一;当对象的引用计数器为0,对象就会被视为垃圾。

优点: 简单、判定垃圾效率高。

缺点:

  • 需要额外的空间存储引用计数器
  • 每当一个引用被赋值给另一个引用时,引用计数器就要进行调整,增加了赋值语句时间
  • 会出现循环引用。比如,对象a引用了对象b,同时对象b也引用了对象a,这就导致两个对象之间循环引用。对象a和对象b的引用都不为0,即使这两个对象已经没有其他引用,由于它们的引用计数都大于0,所以它们就没有办法被回收。如果要解决这个问题就要引入额外机制,这样效率又进一步降低了。

循环引用

引用计数算法在当前主流的JVM中已经没有再被使用了。

简单的例子测试一下

public class ReferenceCountingTest {

    public Object instance = null;

    // 10M 占用内存,便于分析
    private byte[] bytes = new byte[10*1024*1024];

    public static void main(String[] args) {
        ReferenceCountingTest objectA = new ReferenceCountingTest();
        ReferenceCountingTest objectB = new ReferenceCountingTest();

        //互相引用
        objectA.instance = objectB;
        objectB.instance = objectA;

        //切断可达
        objectA = null;
        objectB = null;
        
        //强制进行垃圾回收
        System.gc();
    }
}

ReferenceCountingTest类中有一个10M的byte数组,
objectA.instance = objectBobjectB.instance = objectA导致objectAobjectB互相引用,如果采用引用计数法的话,这两个对象是没法办法进行回收的,并且每个对象占用不少于10M的内存空间。

在VM options 设置参数 -XX:+PrintGC打印GC情况,来看下运行结果是怎样的:

[GC (System.gc())  24381K->1106K(249344K), 0.0009894 secs]
[Full GC (System.gc())  1106K->957K(249344K), 0.0054511 secs]

从结果24381K->1106K可以看到内存从24381K回收到1106K,回收的空间差不多就是objectAobjectB两个对象占用的空间。这也从侧面说明JVM不是采用引用计数算法判定对象是否存活的。

可达性分析算法

可达性分析算法思路是使用一系列根对象(GC Roots)作为起点,从根节点开始向下进行搜索,搜索过的路径称为引用链(Reference Chain),如果某个对象到根节点没有任何引用链相连或者说从根节点到这个对象不可达,则这个对象就被视为“垃圾”。

如上图所示,白色椭圆形Object4、Object5、Object6之间虽然有关联,但是由于没有和GC Roots关联,所以它们被判定为可回收对象。

在Java中有以下7种GC Roots:

  • 虚拟机栈(栈帧中的本地变量表)中引用的对象。比如:方法入参、局部变量等

  • 方法区中常量引用的对象

  • 方法区中类静态属性引用的对象:Java类的引用类型静态变量

  • 通过JNI调用本地代码(nactive code)产生的JNI引用。包括JNI的局部变量或者全局变量

  • 被系统类加载器加载的类,这些类不会被回收。它们可以以静态字段的方式去持有对象。

  • 所有被同步锁(synchronized 关键)持有的对象

  • 被JVM保留用于特殊目的的对象。哪些对象被保留取决于虚拟机的实现,可能的有:系统类加载器、一些重要的异常类、做为异常类处理的被预分配对象或者一些自定义的类加载器。

以上8种GC Roots中前4个比较重要,在面试中也会经常被问到,后3个了解一下即可。

可达性分析算法是目前在动态语言中使用最广泛的算法,目前JVM判断对象是否是垃圾用的都是这种算法。

垃圾的回收

Finalize方法

对象通过可达性分析算法被判定为可回收对象,也不是说对象一定要被回收,对象可以通过重写finalize()方法获得一次“免死”机会。当发生GC的时候,JVM会判断可回收的对象是否调用过finalize()方法,如果调用过finalize()方法,对象将会被回收;反之,如果没有调用过 finalize()方法,会将要调用finalize()方法的对象 F-Queue的队列之中等待调用,在调用时如果对象重写了finalize()方法,可以在finalize()方法中“托关系想办法”让自己和GC Roots搭上关系进行一次自我拯救,比如把自己(this关键字) 赋值给某个类变量或者对象的成员变量,对象就会从即将回收的列表中移除,这样对象就完成了一次自我拯救。在执行完finalize()方法后,还会再判断一次对象是否可达,如果不可达,自我拯救失败,最后还是要被回收的。

要注意的一点是:对象finalize()方法只会调用一次,如果对象自我拯救成功一次,当第二次再发生GC的时候会忽略调用对象的finalize()方法,最后都要被回收。这就是JVM世界的法则,只给对象一次不成为垃圾的机会,如果再次成为垃圾,不好意思那只能被回收了。所以机会只有一次,要好好抓住。

下面通过例子测试一下对象的自我拯救:

public class FinalizeGC {
    private static Object instance;

    public static void main(String[] args) throws InterruptedException {
        instance = new FinalizeGC();

        instance = null;
        //进行第一次垃圾回收
        System.gc();
        //休眠1s
        Thread.sleep(1000);
        if (instance != null) {
            System.out.println("I'm still alive.");
        }else {
            System.out.println("I'm dead.");
        }

        
        instance = null;
        //进行第二次垃圾回收
        System.gc();
        //休眠1s
        Thread.sleep(1000);
        if (instance != null) {
            System.out.println("I'm still alive.");
        }else {
            System.out.println("I'm dead.");
        }
    }

    @Override
    protected void finalize() throws Throwable {
        super.finalize();
        System.out.println("Override finalize method execute");
        instance = this;
    }    
}

运行结果:

Override finalize method execute
I'm still alive.
I'm dead.

从运行结果可以看到对象只被自我拯救一次,第二次自我拯救失败。

让线程休眠Thread.sleep(1000)1s是因为F-Queue的队列中的finalize()方法,会由一条由虚拟机自动建立的、低调度优先级的Finalizer线程去执行它们,休眠是为了等待Finalizer线程去执行finalize()方法。

Thread.sleep(1000)注释掉连续执行多次,你可能会看到如下情况:

Override finalize method execute
I'm dead.
I'm dead.

或者

I'm dead.
Override finalize method execute
I'm dead.

出现上面的原因是finalize()方法执行缓慢,对象还没有自我拯救就会回收了。所以finalize()方法最好不要使用,太不可靠了,也不要想着用finalize()方法进行自我拯救,finalize()能做的所有工作,使用try-finally或者其他方式都可以做得更好、更及时。

方法区回收

方法区的垃圾收集主要回收两部分内容:废弃的常量和不再使用的类型。回收废弃常量与回收Java堆中的对象非常类似。举个常量池中字面量回收的例子,假如一个字符串“suncodernote”曾经进入常量池中,但是当前系统又没有任何一个字符串对象的值是“suncodernote”,换句话说,已经没有任何字符串对象引用常量池中的“suncodernote”常量,且虚拟机中也没有其他地方引用这个字面量。如果在这时发生内存回收,而且垃圾收集器判断确有必要的话,这个“suncodernote”常量就将会被系统清理出常量池。常量池中其他类(接口)、方法、字段的符号引用也与此类似。

判定一个常量是否“废弃”还是相对简单,而要判定一个类型是否属于“不再被使用的类”的条件就比较苛刻了,必须同时满足以下的条件(仅仅是可以,不代表必然,因为还有一些参数可以进行控制):

  1. 该类所有的实例都已经被回收,也就是堆中不存在该类的任何实例。
  2. 加载该类的 ClassLoader 已经被回收。
  3. 该类对应的java.lang.Class对象没有在任何地方被引用,无法在任何地方通过反射访问该类的方法.
  4. 参数控制:-Xnoclassgc参数可以禁用类的垃圾收集(GC),这可以节省一些GC时间,从而缩短应用程序运行期间的中断

总结

本文主要介绍了JVM是通过可达性分析算法来判断对象是否为可回收对象,同时介绍了哪些对象或者是类可作为可达性分析算法的GC Root,最后介绍了对象在Finalize方法中的回收过程和方法区的回收。想学习关于JVM的文章可关注我,会持续更新。

参考书籍:《深入了解JVM虚拟机》

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

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

相关文章

【allegro 17.4软件操作保姆级教程七】布线操作基础之二--铜皮操作

目录 1.1全局动态铜皮参数设置 1.2手动绘制铜皮 1.3手动挖铜 1.4 手动修改铜皮边界 1.5删除孤岛铜皮 1.6动/静态铜皮转换 1.7合并铜皮 1.8平面铺铜和铜皮分割 1.9铜皮颜色设置 今天分享布线操作技巧中的铜皮操作。 1.1全局动态铜皮参数设置 单板上的电源部分、铺地都…

【网安神器篇】——wmic_info信息收集工具

作者名:Demo不是emo 主页面链接:主页传送门 创作初心:舞台再大,你不上台,永远是观众,没人会关心你努不努力,摔的痛不痛,他们只会看你最后站在什么位置,然后羡慕或鄙夷座…

2万字一网通办远程视频踏勘建设方案67页

目 录 1. 建设背景 2. 建设周期 3. 需求分析 3.1. 需求定义 3.1.1. 需求活动目的 3.1.2. 需求分析流程 3.2. 技术需求 3.3. 用户分析 3.3.1. 办事企业 3.3.2. 踏勘人员 3.3.3. 审批人员 3.3.4. 系统管理人员 3.4. 性能需求 3.4.1. 系统架构 3.4.2. 响应时间 3.…

π110E30 单通道数字隔离器兼容代替Si8610BC-B-IS

π110E30 单通道数字隔离器兼容代替Si8610BC-B-IS 。具有出色的性能特征和可靠性,整体性能优于光耦和基于其他原理的数字隔离器产品。 传输通道间彼此独立,可实现多种传输方向的配置,可实现 1.5kV rms 到 5.0kV rms 隔离耐压等级和 DC 到 600…

MyBatis-Plus中查询操作知识点总结

系列文章目录 Mybatis-Plus知识点[MyBatisMyBatis-Plus的基础运用]_心态还需努力呀的博客-CSDN博客 Mybatis-PlusSpringBoot结合运用_心态还需努力呀的博客-CSDN博客 MyBaits-Plus中TableField和TableId用法_心态还需努力呀的博客-CSDN博客 MyBatis-Plus删除操作知识点总结…

【Android App】物联网中指南针、计步器、感光器、陀螺仪的讲解及实战演示(附源码 超详细必看)

需要源码请点赞关注收藏后评论区留言~~~ 一、指南针-磁场传感器 顾名思义,指南针只要找到朝南的方向就好了。 可是在App中并非使用一个方向传感器这么简单,事实上单独的方向传感器已经弃用,取而代之的是利用加速度传感器和磁场传感器。 获得…

区块链工作原理(区块链治理系统、比特币、以太坊、智能合约)

文章目录Blockchain Governance SystemOn-Chain GovernanceOff-Chain GovernanceBitCoin BlockchainEthereum BlockchainProperties of Blockchain SystemSmart ContractsScalability Issues in Blockchain SystemsBlockchain Governance System 每个国家或者城市都有自己的一…

我的第一个网页之----使用HTML编辑器编写HTML文档

HTML篇_三、使用HTML编辑器编写HTML文档 为了满足使用HTML语言进行学习、开发需求我们还需要选择使用一款开发工具来使用。我们可以选择更专业的开发工具来使用,在这里简单介绍几款本人使用过的开发工具,选择你的趁手武器。 一、简述几款HTML编辑器 1…

Proxmox VE 彻底删除本地存储

作者:田逸(formyz) 问题描述 从Proxmox VE web管理后台添加本地存储,如LVM、LVM-Thin(精简逻辑卷)等,有时候可能需要删除这些存储,但其Web管理后台却没有提供菜单或者按钮。要删除这…

手把手教你写Linux线程池

手把手教你写Linux线程池 如果需要线程池源码,关注Linux兵工厂,并由大量Linux资料赠送。 线程池 顾名思义,存储线程的池子。线程池是线程的一种使用模式。在平常业务开发中常规的逻辑是遇到任务然后创建线程去执行。但是线程的频繁创建就类…

NR小区搜索(五)S准则

微信同步更新欢迎关注同名modem协议笔记 UE根据支持的频段进行小区搜索过程,检测PSS/SSS->PBCH,然后就可以读到MIB,根据MIB中的pdcch-ConfigSIB1,可以找到CORESET0 和SearchSpace0的信息,进而可以确定一块时频域资…

软件测试 -- 进阶 6 软件缺陷

上工治未病之病,中工治欲病之病,下工治已病之病。-- 孙思邈 .《千金方药方》 释译:未病之病:未病,未发之病(及早干预,防止病发);欲病之病:小病&#xff0…

MIR7创建预制发票BAPI

1、事务代码MIR7 前台输入采购订单等相关字段进行开票 2、代码实现 调用BAPI:BAPI_INCOMINGINVOICE_PARK创建发票 "--------------------斌将军-------------------- DATA:ls_headerdata TYPE bapi_incinv_create_header,lv_invoicedocnumber LIKE ba…

桌面画图工具:Pointofix(fertig)

Pointofix桌面画图工具 Pointofix - der virtuelle Textmarker fr Ihren Bildschirm - Freeware 一、软件下载 官方网址https://www.pointofix.de/ 二、进入下载页面,需要下载安装文件和语言包两个文件 三、网站还提供了一个语言设置小程序,但我没用 …

JavaSE笔记——抽象类和接口

文章目录前言一、抽象类和方法二、接口创建1.默认方法2.多继承3.接口中的静态方法三、抽象类和接口四、完全解耦五、使用继承扩展接口六、接口适配七、接口字段八、接口和工厂方法模式总结前言 接口和抽象类提供了一种将接口与实现分离的更加结构化的方法。 一、抽象类和方法 …

传奇外网架设教程

外网架设前需准备: 准备工具:传奇版本源码,服务器,备案域名,DBC数据库,周年客户端 服务器和备案域名需要自备或者租用,这东西自己造不出来!!! 其他的工具,…

Flink被阿里收购4年,最开心的却是Spark背后的Databricks

最近,Flink Forward Asia(FFA)峰会成功举行,有关Flink的讨论,又开始在国内热闹起来。 2022 年,Apache Flink 社区保持快速发展:GitHub Star 数突破 2 万,单月下载量突破 1400 万次&…

学习总结 | 下一代人工智能

文章目录 一、前言二、底层逻辑三、六大维度今后发展的方向是第三代人工智能,最主要的措施就是把第一代人工智能知识驱动的方法和第二代人工智能数据驱动的方法结合起来,发展安全、可信、可靠和可扩展的人工智能技术,从而推动人工智能的创新应用。 一、前言 中国科学院院士…

iTOP3A5000开发板多路PCIE、SATA、USB3.0等

iTOP3A5000开发板多路PCIE、SATA、USB3.0等 桥片:支持PCIE3.0、USB3.0、SATA3.0、显示接口2路、HDMI和1路VGA、可直接连显示器,另外内置一个网络PHY,片内集成了自研GPU、搭配32位DDR4显存接口,支持16GB显存容量。 底板引出多路PCI…

第3关:添加数据、删除数据、删除表

为了完成本关任务,你需要掌握:1.如何使用HBase shell命令添加数据、2.如何使用命令删除表。 首先启动HBASE 启动HBASEshell 添加数据 我们来给上一关创建的test表的列data添加一些数据: hbase(main):002:0> create test,data Created t…