如何判断对象是否该被回收(引用计数法、可达性分析算法)

简介: 概述垃圾收集器需要完那些内存需要回收?什么时候回收?如何回收?

概述


垃圾收集器需要完成的三件事

那些内存需要回收?

什么时候回收?

如何回收?

那些需要进行GC

程序计数器、虚拟机栈、本地方法栈3个区域随线程而生,随线程灭而灭,栈中的栈帧随着方法的进入和退出有条不紊的进行出栈和入栈操作。每一个栈帧中分配多少内存基本上是在类结构确定下来时就已知(尽管在运行期间会由即时编译器进行一些优化,但在基于概念模型的讨论里,大体上可以认为是已知的),因此这几个区域内存的分配和回收都具备确定性,在这几个区域内就不需要过多考虑回收的问题,当方法结束或者线程结束时,内存自然就随着回收了

Java堆和方法区这两个区域则有着很显著的不确定性:一个接口的多个实现类需要的内存可能会不一样,一个方法所执行的不同条件分支所需要的内存也可能不一样,只有处于运行期间,我们才能知道这个程序究竟会创建那些对象。


如何判断对象是否该被回收?


引用计数法

在对象里面添加一个引用计数器,每当有一个对象引用它时,计数器值就加一;每当引用失效,计数器的值就减一;任何时刻计数器为零的对象就是不可能再被使用的

/**
 * @className: JVM_1
 * @description: 测试JVM是否使用引用计数法
 * @author: 热爱生活の李
 * @since: 2022/5/20 14:52
 */
public class JVM_1 {
    public Object instance = null;
    private static final int _1MB = 1024 * 1024;
    public static void main(String[] args) {
        JVM_1 objectA = new JVM_1();
        JVM_1 onjectB = new JVM_1();
        objectA.instance = onjectB;
        onjectB.instance = objectA;
        objectA = null;
        onjectB = null;
        System.gc();
    }
}

5de81675be124a8fa705ca3aba37eb7f.png

添加GC日志打印

运行配置里加VM option

参数为 -XX:+PrintGCDetails


缺点

可能会造成循环引用,导致无法回收,例如:上面对象objectA和objectB都有字段instance,objectA.instance = objectB,objectB.instance = objectA,除此之外,这两个对象再无其他任何引用,实际上这两个对象不可能被访问,但是他们互相引用对方,导致引用计数器不为0,就无法回收它们。


可达性分析算法

通过一系列称为“ GC Roots ” 的根对象作为起始节点集,从这些节点开始,根据引用关系向下搜索,搜索过程所走过的路径称为“ 引用链 ” ,如果某个对象到GC Roots 间没有任何链相连,或者用图论的话来说就是从GC Roots到这个对象不可达时,则证明对象不可能再被使用


那些可以作为GC Roots对象


固定作为GC Roots

1、在虚拟机栈中引用的对象(各个线程被调用的方法栈中的参数、局部变量、临时变量)

2、在方法区中类静态属性引用的对象(Java类的引用类型静态变量)

3、在方法区常量引用的对象(字符串常量池String Table的引用)

4、在本地方法栈中引用的对象(Native方法)

5、Java虚拟机内部的引用(基本数据类型对应的Class对象,一些常驻的异常对象比如NullPointException,还有系统类加载器)

6、所有被同步锁(synchronized关键字)持有的对象

7、反映Java虚拟机内部情况的JMXBean、JVMTL注册的回调、本地代码缓存等


根据用户所选择的垃圾收集器以及当前回收的内存区域不同,还可以用其他对象"临时性"地加入

引用

Java对引用的概念进行了扩充,将引用分为强引用(Strongly Reference)、软引用(Soft Reference)、弱引用(Weak Reference)和虚引用(Phantom Reference)


强引用: 指在程序代码之间存在的引用赋值,即类型Object obj = new Object() 这种引用关系。无论任何关系下,只要强引用关系还存在,垃圾收集器就不会回收掉被引用的对象

软引用: 用来描述一些还有用,但非必须的对象。只被软引用关联着的对象,在系统将要发生内存溢出异常前,会把这些对象列进回收范围之中进行第二次回收,如过这次回收还是没有足够的内存,才会抛出内存溢出异常。

弱引用: 用来描述那些非必要对象,但是它的强度比软引用更弱一些,关联的对象只能生存到下一次垃圾收集为止。当垃圾收集器开始工作,无论当前内存是否足够,都会回收掉只被弱引用关联的对象

虚引用: 最弱的一种引用关系。一个对象是否有虚引用的存在,完全不会对其生存时间构成影响,也无法通过虚引用来取得一个对象实列。为一个对象设置虚引用关联的唯一目的只是为了能在这个对象被收集器回收的时候收到一个系统通知。

生存还是死亡

即使是在可达性分析算法中判断为不可达的对象也不是非死不可,要真正宣告一个对象死亡,至少要经历两次标记过程。

11707d60058640d5920251fae2731803.png

package JVM;
/**
 * @className: JVM_2
 * @description: 测试finalize()方法
 * @author: 热爱生活の李
 * @since: 2022/5/20 15:08
 */
public class JVM_2 {
    public static JVM_2 instance = null;
    @Override
    protected void finalize() throws Throwable {
        super.finalize();
        System.out.println("finalize 方法执行了 !!!");
        JVM_2.instance = this;
    }
    public static void main(String[] args) throws InterruptedException {
        instance = new JVM_2();
        instance = null;
        System.gc();
        // finalizer方法优先级很低
        Thread.sleep(5000);
        if(instance != null){
            System.out.println("我还活着 !!!");
        }else {
            System.out.println("我死了 !!!");
        }
        instance = null;
        System.gc();
        Thread.sleep(5000);
        if(instance != null){
            System.out.println("我还活着 !!!");
        }else {
            System.out.println("我死了 !!!");
        }
    }
}

0a35048a218c49db9ee7ce18db2be970.pngfinalize()方法只会被调用一次


回收方法区

方法区的垃圾收集主要回收两部分内容:废弃的常量和不再使用的类型


回收废弃的常量

假如一个字符串“Java” 曾经进入常量池,但是当前系统又没有任何一个字符串对象的值是“Java”,换句话说已经没有任何字符串对象引用常量池中的”Java“常量,且虚拟机中也没有其他地方引用这个字面量。如果这时发生内存回收,而且垃圾收集器判断有必要的话,这个”Java“常量就会被清理出常量池。常量池中其他类(接口)、方法、字段的符号引用也与此类似


回收类型的判断条件

1、该类的所有实例化都已经被回收,也就是Java堆中不存在该类及其任何派生子类的实例

2、加载该类的类加载器已经被回收

3、该类对应的java.lang.Class对象没有在任何地方被引用,无法在任何地方通过反射访问该类的方法


Java虚拟机被允许对满足上述三个条件的无用类进行回收,这里说的仅仅是被允许,而并不是和对象一样,没有引用就被必然被回收。

关于是否要对类型进行回收,HotSpot虚拟机提供了 -Xnoclassgc 参数进行控制,还可以使用 -verbose:class 以及 -XX:+TraceClassLoading、-XX:TraceClassUnLoading 查看类加载和卸载信息


目录
打赏
0
0
0
0
4
分享
相关文章
从公布的前十一批其他算法备案通过名单分析
2025年3月12日,国家网信办发布算法备案信息,深度合成算法通过395款,其他算法45款。前10次备案中,深度合成算法累计3234款,其他类别647款。个性化推送类占比49%,涵盖电商、资讯、视频推荐;检索过滤类占31.53%,用于搜索优化和内容安全;调度决策类占9.12%,集中在物流配送等;排序精选类占8.81%,生成合成类占1.55%。应用领域包括电商、社交媒体、物流、金融、医疗等,互联网科技企业主导,技术向垂直行业渗透,内容安全和多模态技术成新增长点。未来大模型检索和多模态生成或成重点。
从公布的前十一批其他算法备案通过名单分析
从第十批算法备案通过名单中分析算法的属地占比、行业及应用情况
2025年3月12日,国家网信办公布第十批深度合成算法通过名单,共395款。主要分布在广东、北京、上海、浙江等地,占比超80%,涵盖智能对话、图像生成、文本生成等多行业。典型应用包括医疗、教育、金融等领域,如觅健医疗内容生成算法、匠邦AI智能生成合成算法等。服务角色以面向用户为主,技术趋势为多模态融合与垂直领域专业化。
境内深度合成服务算法备案通过名单分析报告
本报告基于《境内深度合成服务算法备案通过名单》,分析了2023年6月至2025年3月公布的10批备案数据,涵盖属地分布、行业应用及产品形式等多个维度。报告显示,深度合成算法主要集中于经济发达地区,如北京、广东、上海等地,涉及教育、医疗、金融、娱乐等多行业。未来趋势显示技术将向多模态融合、行业定制化和安全合规方向发展。建议企业加强技术研发、拓展应用场景、关注政策动态,以在深度合成领域抢占先机。此分析旨在为企业提供参考,助力把握技术发展机遇。
境内深度合成服务算法备案通过名单分析报告
企业监控软件中 Go 语言哈希表算法的应用研究与分析
在数字化时代,企业监控软件对企业的稳定运营至关重要。哈希表(散列表)作为高效的数据结构,广泛应用于企业监控中,如设备状态管理、数据分类和缓存机制。Go 语言中的 map 实现了哈希表,能快速处理海量监控数据,确保实时准确反映设备状态,提升系统性能,助力企业实现智能化管理。
35 3
从第九批深度合成备案通过公示名单分析算法备案属地、行业及应用领域占比
2024年12月20日,中央网信办公布第九批深度合成算法名单。分析显示,教育、智能对话、医疗健康和图像生成为核心应用领域。文本生成占比最高(57.56%),涵盖智能客服、法律咨询等;图像/视频生成次之(27.32%),应用于广告设计、影视制作等。北京、广东、浙江等地技术集中度高,多模态融合成未来重点。垂直行业如医疗、教育、金融加速引入AI,提升效率与用户体验。
基于哈希表的文件共享平台 C++ 算法实现与分析
在数字化时代,文件共享平台不可或缺。本文探讨哈希表在文件共享中的应用,包括原理、优势及C++实现。哈希表通过键值对快速访问文件元数据(如文件名、大小、位置等),查找时间复杂度为O(1),显著提升查找速度和用户体验。代码示例展示了文件上传和搜索功能,实际应用中需解决哈希冲突、动态扩容和线程安全等问题,以优化性能。
Java中的算法优化与复杂度分析
在Java开发中,理解和优化算法的时间复杂度和空间复杂度是提升程序性能的关键。通过合理选择数据结构、避免重复计算、应用分治法等策略,可以显著提高算法效率。在实际开发中,应该根据具体需求和场景,选择合适的优化方法,从而编写出高效、可靠的代码。
73 6
基于生物地理算法的MLP多层感知机优化matlab仿真
本程序基于生物地理算法(BBO)优化MLP多层感知机,通过MATLAB2022A实现随机数据点的趋势预测,并输出优化收敛曲线。BBO模拟物种在地理空间上的迁移、竞争与适应过程,以优化MLP的权重和偏置参数,提升预测性能。完整程序无水印,适用于机器学习和数据预测任务。
101 31
基于LSB最低有效位的音频水印嵌入提取算法FPGA实现,包含testbench和MATLAB对比
本项目展示了一种基于FPGA的音频水印算法,采用LSB(最低有效位)技术实现版权保护与数据追踪功能。使用Vivado2019.2和Matlab2022a开发,完整代码含中文注释及操作视频。算法通过修改音频采样点的最低有效位嵌入水印,人耳难以察觉变化。然而,面对滤波或压缩等攻击时,水印提取可能受影响。该项目运行效果无水印干扰,适合实时应用场景,核心逻辑简单高效,时间复杂度低。
基于GA遗传算法的拱桥静载试验车辆最优布载matlab仿真
本程序基于遗传算法(GA)实现拱桥静载试验车辆最优布载的MATLAB仿真,旨在自动化确定车辆位置以满足加载效率要求(0.95≤ηq≤1.05),目标是使ηq尽量接近1,同时减少车辆数量和布载耗时。程序在MATLAB 2022A版本下运行,展示了工况1至工况3的测试结果。通过优化模型,综合考虑车辆重量、位置、类型及车道占用等因素,确保桥梁关键部位承受最大荷载,从而有效评估桥梁性能。核心代码实现了迭代优化过程,并输出最优布载方案及相关参数。

热门文章

最新文章