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

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

概述


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

那些内存需要回收?

什么时候回收?

如何回收?

那些需要进行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 查看类加载和卸载信息


相关文章
|
3天前
|
机器学习/深度学习 自然语言处理 算法
Python遗传算法GA对长短期记忆LSTM深度学习模型超参数调优分析司机数据|附数据代码
Python遗传算法GA对长短期记忆LSTM深度学习模型超参数调优分析司机数据|附数据代码
|
3天前
|
机器学习/深度学习 算法 数据可视化
Matlab决策树、模糊C-均值聚类算法分析高校教师职称学历评分可视化
Matlab决策树、模糊C-均值聚类算法分析高校教师职称学历评分可视化
|
3天前
|
算法 搜索推荐 数据挖掘
MATLAB模糊C均值聚类FCM改进的推荐系统协同过滤算法分析MovieLens电影数据集
MATLAB模糊C均值聚类FCM改进的推荐系统协同过滤算法分析MovieLens电影数据集
|
3天前
|
算法 数据可视化 数据挖掘
数据分享|R语言改进的K-MEANS(K-均值)聚类算法分析股票盈利能力和可视化
数据分享|R语言改进的K-MEANS(K-均值)聚类算法分析股票盈利能力和可视化
|
3天前
|
数据采集 存储 算法
数据分享|Weka数据挖掘Apriori关联规则算法分析用户网购数据
数据分享|Weka数据挖掘Apriori关联规则算法分析用户网购数据
|
3天前
|
机器学习/深度学习 数据采集 算法
共享单车需求量数据用CART决策树、随机森林以及XGBOOST算法登记分类及影响因素分析
共享单车需求量数据用CART决策树、随机森林以及XGBOOST算法登记分类及影响因素分析
|
3天前
|
移动开发 算法 数据可视化
数据分享|Spss Modeler关联规则Apriori模型、Carma算法分析超市顾客购买商品数据挖掘实例
数据分享|Spss Modeler关联规则Apriori模型、Carma算法分析超市顾客购买商品数据挖掘实例
|
3天前
|
算法 数据安全/隐私保护 计算机视觉
基于二维CS-SCHT变换和LABS方法的水印嵌入和提取算法matlab仿真
该内容包括一个算法的运行展示和详细步骤,使用了MATLAB2022a。算法涉及水印嵌入和提取,利用LAB色彩空间可能用于隐藏水印。水印通过二维CS-SCHT变换、低频系数处理和特定解码策略来提取。代码段展示了水印置乱、图像处理(如噪声、旋转、剪切等攻击)以及水印的逆置乱和提取过程。最后,计算并保存了比特率,用于评估水印的稳健性。
|
1天前
|
算法
MATLAB|【免费】融合正余弦和柯西变异的麻雀优化算法SCSSA-CNN-BiLSTM双向长短期记忆网络预测模型
这段内容介绍了一个使用改进的麻雀搜索算法优化CNN-BiLSTM模型进行多输入单输出预测的程序。程序通过融合正余弦和柯西变异提升算法性能,主要优化学习率、正则化参数及BiLSTM的隐层神经元数量。它利用一段简单的风速数据进行演示,对比了改进算法与粒子群、灰狼算法的优化效果。代码包括数据导入、预处理和模型构建部分,并展示了优化前后的效果。建议使用高版本MATLAB运行。
|
3天前
|
算法 计算机视觉
基于高斯混合模型的视频背景提取和人员跟踪算法matlab仿真
该内容是关于使用MATLAB2013B实现基于高斯混合模型(GMM)的视频背景提取和人员跟踪算法。算法通过GMM建立背景模型,新帧与模型比较,提取前景并进行人员跟踪。文章附有程序代码示例,展示从读取视频到结果显示的流程。最后,结果保存在Result.mat文件中。