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

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

概述


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

那些内存需要回收?

什么时候回收?

如何回收?

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


相关文章
|
4天前
|
算法 Java 测试技术
算法分析(蛮力法与减治算法应用实验报告)
这篇文章是关于算法分析的实验报告,介绍了如何使用蛮力法解决背包问题,并通过伪代码和Java代码实现,同时分析了其时间效率;还介绍了基于减治法思想实现的二叉查找树的插入与查找,同样提供了伪代码、Java源代码实现和时间效率分析,最后展示了测试结果截图。
算法分析(蛮力法与减治算法应用实验报告)
|
11天前
|
机器学习/深度学习 数据采集 算法
【优秀python算法毕设】基于python时间序列模型分析气温变化趋势的设计与实现
本文介绍了一个基于Python的时间序列模型,用于分析和预测2021-2022年重庆地区的气温变化趋势,通过ARIMA和LSTM模型的应用,揭示了气温的季节性和趋势性变化,并提供了对未来气温变化的预测,有助于气象预报和相关决策制定。
【优秀python算法毕设】基于python时间序列模型分析气温变化趋势的设计与实现
|
2天前
|
机器学习/深度学习 人工智能 算法
【人工智能】传统语音识别算法概述,应用场景,项目实践及案例分析,附带代码示例
传统语音识别算法是将语音信号转化为文本形式的技术,它主要基于模式识别理论和数学统计学方法。以下是传统语音识别算法的基本概述
7 2
|
4天前
|
机器学习/深度学习 存储 算法
算法时间复杂度分析
这篇文章讲解了如何分析算法的时间复杂度,包括关注循环执行次数最多的代码段、总复杂度的确定、嵌套代码复杂度的计算方法,并提供了大O阶的推导步骤和常见时间复杂度的列表,同时还介绍了空间复杂度的概念及其重要性。
|
6天前
|
编解码 算法 Linux
Linux平台下RTSP|RTMP播放器如何跟python交互投递RGB数据供视觉算法分析
在对接Linux平台的RTSP播放模块时,需将播放数据同时提供给Python进行视觉算法分析。技术实现上,可在播放时通过回调函数获取视频帧数据,并以RGB32格式输出。利用`SetVideoFrameCallBackV2`接口设定缩放后的视频帧回调,以满足算法所需的分辨率。回调函数中,每收到一帧数据即保存为bitmap文件。Python端只需读取指定文件夹中的bitmap文件,即可进行视频数据的分析处理。此方案简单有效,但应注意控制输出的bitmap文件数量以避免内存占用过高。
|
10天前
|
算法
PID算法原理分析及优化
今天为大家介绍一下经典控制算法之一的PID控制方法。PID控制方法从提出至今已有百余年历史,其由于结构简单、易于实现、鲁棒性好、可靠性高等特点,在机电、冶金、机械、化工等行业中应用广泛。 在大学期间,参加的智能汽车竞赛中就使用到了PID经典控制算法,对于智能小车的调试更加的方便。 一、PID原理 PID控制方法将偏差的比例(proportional)、积分(integral)、微分(derivative)通过线性组合构成控制量,对被控对象进行控制。 常规的PID控制系统如图所示: 系统的输入r(t)为控制量的目标输出值,输出y(t)为控制量的实际输出值,e(t)为输出量目标值与实际值
24 1
|
12天前
|
机器学习/深度学习 数据采集 算法
基于SVm和随机森林算法模型的中国黄金价格预测分析与研究
本文通过运用支持向量机(SVM)、决策树和随机森林算法,结合历史黄金价格数据和特征工程,建立了中国黄金价格的预测模型,并通过模型训练、评估及可视化分析,为黄金市场投资者和分析师提供了基于机器学习算法的预测方法和决策支持。
|
6天前
|
算法
基于模糊控制算法的倒立摆控制系统matlab仿真
本项目构建了一个基于模糊控制算法的倒立摆控制系统,利用MATLAB 2022a实现了从不稳定到稳定状态的转变,并输出了相应的动画和收敛过程。模糊控制器通过对小车位置与摆的角度误差及其变化量进行模糊化处理,依据预设的模糊规则库进行模糊推理并最终去模糊化为精确的控制量,成功地使倒立摆维持在直立位置。该方法无需精确数学模型,适用于处理系统的非线性和不确定性。
基于模糊控制算法的倒立摆控制系统matlab仿真
|
5天前
|
机器学习/深度学习 算法 定位技术
MATLAB - 遗传算法(GA)求解旅行商问题(TSP)
MATLAB - 遗传算法(GA)求解旅行商问题(TSP)
11 3
|
6天前
|
算法
基于多路径路由的全局感知网络流量分配优化算法matlab仿真
本文提出一种全局感知网络流量分配优化算法,针对现代网络中多路径路由的需求,旨在均衡分配流量、减轻拥塞并提升吞吐量。算法基于网络模型G(N, M),包含N节点与M连接,并考虑K种不同优先级的流量。通过迭代调整每种流量在各路径上的分配比例,依据带宽利用率um=Σ(xm,k * dk) / cm来优化网络性能,确保高优先级流量的有效传输同时最大化利用网络资源。算法设定收敛条件以避免陷入局部最优解。

热门文章

最新文章