如何判断一个Java对象是否存活?

本文深入探讨了Java中判断对象是否存活的两种关键方法:引用计数法与引用链法(可达性分析法)。详细解析了每种方法的工作原理、优缺点及应用场景,帮助读者理解垃圾回收机制的核心。
首先为什么要去判断一个Java对象是否存活呢?

     这是因为判断一个Java对象是否存活对于垃圾回收、防止内存泄漏等十分重要,垃圾收集器对 Java堆里的对象是否存活进行判断,Java对象存活不回收,死亡则回收。

    判断方式
            在Java虚拟机中,判断对象是否存活有两种方法:
                       1、引用计数法
                       2、引用链法(可达性分析法)

一、引用计数法

1.1方法描述:

      1.给Java对象添加一个计数器引用计数器

      2.每当有一个地方引用它时,计数器+1,引用失效则-1.

1.2判断对象存活准则

      当计数器不为 0 时,判断该对象存活;否则判断为死亡(计数器 = 0)。
1.3 优点
      1.实现简单
      2.判断高效
1.4 缺点
无法解决对象间相互循环引用的问题,会进入死循环(也就是该算法存在着判断逻辑的漏洞)

二、引用链法(可达性分析法)

2.1引用链法主要有三个步骤

      1.可达性分析

      2.第一次标记 & 筛选

      3.第二次标记 & 筛选

2.2方法描述

1、可达性分析

      将一系列的 GC Roots 对象作为起点,从这些起点开始向下搜索。

      GC Roots是什么?常说的GC(Garbage Collector) roots,特指的是垃圾收集器(Garbage Collector)的对象,GC会收集那些不是GC roots且没有被GC roots引用的对象。
可作为 GC Roots 的对象有:
      1.Java虚拟机栈(栈帧的本地变量表)中引用的对象
      2.本地方法栈 中 JNI引用对象
      3.方法区 中常量、类静态属性引用的对象

2.3方法流程

当一个对象到 GC Roots 没有任何引用链相连时,则判断该对象不可达
判断一个对象真正死亡,还需要经过如下两个阶段:

      1.第一次标记 & 筛选

      2.第二次标记 & 筛选

2、第一次标记 & 筛选

标记:
      a.不筛选:继续留在 ”即将回收“的集合里,等待回收;
      b. 筛选:从 ”即将回收“的集合取出
筛选的标准:(该对象是否有必要执行 finalize()方法)
      a.若有必要执行(人为设置),则筛选出来,进入下一阶段(第二次标记 & 筛选);
      b.若没必要执行,判断该对象死亡,不筛选 并等待回收(若没有finalize()方法则认为没必要执行)

3、第二次标记 & 筛选

(当对象经过了第一次的标记 & 筛选,会被进行第二次标记 & 准备被进行筛选)
方法描述:
      该对象会被放到一个 F-Queue 队列中,并由 虚拟机自动建立、优先级低的Finalizer 线程去执行 队列中该对象的finalize()。有两点需要注意:
      ①:finalize()只会被执行一次
      ②:并不承诺等待finalize()运行结束。这是为了防止 finalize()执行缓慢 / 停止,使得 F-Queue队列其他对象永久等待。
筛选标准:
      在执行finalize()过程中,若对象依然没与引用链上的GC Roots 直接关联或间接关联(即关联上与GC Roots 关联的对象),那么该对象将被判断死亡,不筛选(留在”即将回收“集合里并等待回收

在主流的商用程序语言(如 Java、C#)的主流实现中,判断对象是否存活主要有两种方法:引用计数算法和可达性分析算法[^2][^4]。 ### 引用计数算法 在对象中添加一个引用计数器,每有地方引用它,计数器的值加 1,若引用失效,则计数器的值减 1,任何时刻计数器为 0 的对象就是不再被使用的对象。这种算法实现简单,判定效率高,但很难解决对象之间相互循环引用的问题。以下是一个循环引用的案例代码: ```java public class test { private Object instence; public test() { //开辟一片 20MB 大小的内存,便于形象的看出释放效果 byte [] b = new byte[20 * 1024 * 1024]; } public static void main(String[] args) { //创建 m1、m2.此时栈中有两个引用 m1、m2 分别指向堆中的两个 Main 中。 test m1 = new test(); test m2 = new test(); //让堆中 m1 指向 m2 m1.instence = m2; //让堆中 m2 指向 m1 m2.instence = m1; //将栈对堆的引用断开 m1 = null ; m2 = null ; //主动调用垃圾回收机制 System.gc(); } } ``` 在这个案例中,`m1` 和 `m2` 相互引用,即使栈对它们的引用断开,由于计数器不为 0,引用计数算法无法回收它们,从而导致内存泄漏[^1][^4]。 ### 可达性分析算法 以一系列称为“GC Roots”的对象作为起始点,从这些节点开始向下搜索,搜索所走过的路径称为引用链。当一个对象到 GC Roots 没有任何引用链相连(即从 GC Roots 到这个对象不可达)时,说明此对象是不可用的,将会被判定为可回收的对象。在 Java 语言中,能被认为是 GC Roots 的对象包括以下几种: 1. 虚拟机栈(栈帧中的本地变量表)中的引用的对象; 2. 本地方法栈中 JNI(Native 方法)引用的对象; 3. 方法区中类静态属性引用的对象; 4. 方法区中常量引用的对象; 5. 所有被同步锁(synchronized 关键字)持有的对象[^2][^3][^4]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值