前言

JVM堆中几乎存放了所有对象的实例,那么垃圾收集器怎么确定哪些对象还“存活”着,哪些已经“死去”呢?本文主要介绍判断对象是否存活算法引用计数算法和可达性分析算法。

引用计数算法

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

引用计数算法的缺陷-相互引用代码示例:

/*** testGC()方法执行后,objA和objB会不会被GC呢? */
public class ReferenceCountingGC {public Object instance = null;private static final int _1MB = 1024 * 1024;/*** 这个成员属性的唯一意义就是占点内存,以便能在GC日志中看清楚是否有回收过 */private byte[] bigSize = new byte[8 * _1MB];public static void testGC() {ReferenceCountingGC objA = new ReferenceCountingGC();ReferenceCountingGC objB = new ReferenceCountingGC();objA.instance = objB;objB.instance = objA;objA = null;objB = null; // 假设在这行发生GC,objA和objB是否能被回收?System.gc();}public static void main(String[] args) {testGC();}
}

启动参数设置:

//打印GC信息
-XX:+PrintGCDetails

运行结果:

[GC (System.gc()) [PSYoungGen: 22938K->1183K(76288K)] 22938K->1191K(251392K), 0.0120565 secs] [Times: user=0.08 sys=0.00, real=0.01 secs]
[Full GC (System.gc()) [PSYoungGen: 1183K->0K(76288K)] [ParOldGen: 8K->1036K(175104K)] 1191K->1036K(251392K), [Metaspace: 3171K->3171K(1056768K)], 0.0178115 secs] [Times: user=0.06 sys=0.02, real=0.02 secs]
HeapPSYoungGen      total 76288K, used 1966K [0x000000076b400000, 0x0000000770900000, 0x00000007c0000000)eden space 65536K, 3% used [0x000000076b400000,0x000000076b5eb9e0,0x000000076f400000)from space 10752K, 0% used [0x000000076f400000,0x000000076f400000,0x000000076fe80000)to   space 10752K, 0% used [0x000000076fe80000,0x000000076fe80000,0x0000000770900000)ParOldGen       total 175104K, used 1036K [0x00000006c1c00000, 0x00000006cc700000, 0x000000076b400000)object space 175104K, 0% used [0x00000006c1c00000,0x00000006c1d031f0,0x00000006cc700000)Metaspace       used 3178K, capacity 4556K, committed 4864K, reserved 1056768Kclass space    used 339K, capacity 392K, committed 512K, reserved 1048576K
Disconnected from the target VM, address: '127.0.0.1:56351', transport: 'socket'

PSYoungGen表示这是一次新生代GC,[PSYoungGen: 22938K->1183K(76288K)] 表示新生代GC情况,22938K->1191K(251392K), 0.0120565 secs表示堆的总体空间情况和耗时,[Times: user=0.08 sys=0.00, real=0.01 secs] user表示用户态CPU耗时,sys表示系统CPU耗时,real表示GC实际经历的时间。

从运行结果中,看出年轻代used 1966K ,意味虚拟机不是通过引用计数算法来判断对象 是否存活的。

可达性分析算法

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

如图下图所示,对象object 5、object 6、object 7虽然互有关联,但是它们到GC Roots是不可达的, 因此它们将会被判定为可回收的对象。

GC Roots的对象包括以下几种:

·在虚拟机栈(栈帧中的本地变量表)中引用的对象,譬如各个线程被调用的方法堆栈中使用到的 参数、局部变量、临时变量等。

·在方法区中类静态属性引用的对象,譬如Java类的引用类型静态变量。

·在方法区中常量引用的对象,譬如字符串常量池(String Table)里的引用。

·在本地方法栈中JNI(即通常所说的Native方法)引用的对象。

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

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

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

除了这些固定的GC Roots集合以外,根据用户所选用的垃圾收集器以及当前回收的内存区域不 同,还可以有其他对象“临时性”地加入,共同构成完整GC Roots集合。

可达性分析回收过程

·如果对象A到GC Roots没有引用链,则进行第一次标记;

进行筛选,判断此对象是否有必要执行finalize()方法 ;

如果对象A没有重写finalize()方法,或者finalize()方法已经被虚拟机调用过,则虚拟机视为"没有必要执行",A被判定为不可触及的;

如果对象A重写了finalize()方法,且还未执行过,那么A会被插入到F-Queue队列中,由一个虚拟机自动创建的、低优先级的finalizer线程触发其finalize()方法执行;

finalize()方法是对象逃脱死亡的最后机会,稍后GC会对F-Queue队列中的对象进行第二次标记;

如果这期间A在finalize()方法中与引用链上的任何一个对象建立了联系,那么在第二次标记时,A会被移除"即将回收"集合;

如果对象这时候还没有逃脱,那基本上它就真的要被回收了。

自我拯救的代码演示:

/**** 此代码演示了两点:* 1.对象可以在被GC时自我拯救。* 2.这种自救的机会只有一次,因为一个对象的finalize()方法最多只会被系统自动调用一次*/
public class FinalizeEscapeGC {public static FinalizeEscapeGC SAVE_HOOK = null;public void isAlive() {System.out.println("yes, i am still alive :)");}@Overrideprotected void finalize() throws Throwable {super.finalize();System.out.println("finalize method executed!");FinalizeEscapeGC.SAVE_HOOK = this;}public static void main(String[] args) throws Throwable {SAVE_HOOK = new FinalizeEscapeGC();//对象第一次成功拯救自己SAVE_HOOK = null;System.gc();// 因为Finalizer方法优先级很低,暂停0.5秒,以等待它Thread.sleep(500);if (SAVE_HOOK != null) {SAVE_HOOK.isAlive();} else {System.out.println("no, i am dead :(");}// 下面这段代码与上面的完全相同,但是这次自救却失败了SAVE_HOOK = null;System.gc();// 因为Finalizer方法优先级很低,暂停0.5秒,以等待它Thread.sleep(500);if (SAVE_HOOK != null) {SAVE_HOOK.isAlive();} else {System.out.println("no, i am dead :(");}}
}

运行结果:

finalize method executed!
yes, i am still alive :)
no, i am dead :(


点赞 收藏 关注
代码传递思想,技术创造回响。

【JVM】引用计数和可达性分析算法详解相关推荐

  1. JVM—引用计数和可达性分析算法(存活性判断)

    1 引用计数算法 1.1 算法思想   给对象中添加一个引用计数器,每当有一个地方引用它时,计数器值就加1:   当引用失效时,计数器值就减1:   任何时候计数器为0时的对象就是不能再被使用. 1. ...

  2. 对象是否要被回收(引用计数和可达性分析算法)

    java堆和方法区主要存放各种类型的对象(方法区中也存储一些静态变量和全局常量等信息),那么我们在使用GC对其进行回收的时候首先要考虑的就是如何判断一个对象是否应该被回收.也就是要判断一个对象是否还有 ...

  3. 引用计数器法 可达性分析算法_面试官:你说你熟悉jvm?那你讲一下并发的可达性分析...

    持续输出原创文章,点击蓝字关注我吧 上面这张图是我还是北漂的时候,在鼓楼附近的胡同里面拍的. 那天刚刚下完雨,路过这个地方的时候,一瞬间就被这五颜六色的门板和自行车给吸引了,于是拍下了这张图片.看到这 ...

  4. JVM底层原理+四大垃圾回收算法详解-周阳老师

    转载自,感谢原作者:https://www.jianshu.com/p/9e6841a895b4 注意:垃圾回收算法周阳老师讲的有错误,具体在p19,四大垃圾回收算法为复制算法.标记-整理算法.标记- ...

  5. 3.内存分配、逃逸分析与栈上分配、直接内存和运行时常量池、基本类型的包装类和常量池、TLAB、可达性分析算法(学习笔记)

    3.JVM内存分配 3.1.内存分配概述 3.2.内存分配–Eden区域 3.3.内存分配–大对象直接进老年代 3.3.1.背景 3.3.2.解析 3.4.内存分配–长期存活的对象进去老年代 3.5. ...

  6. JVM 的三色标记算法详解

    本文来说下关于JVM 的三色标记算法. 文章目录 三色标记算法概述 引用计数&可达性分析 分代收集 什么是卡表 卡表的问题 写屏障 伪共享 三色标记算法 基本算法 三色标记算法缺陷 多标 漏标 ...

  7. JAVA之JVM垃圾回收(GC)机制详解

    一.为什么需要垃圾回收 如果不进行垃圾回收,内存迟早都会被消耗空,因为我们在不断的分配内存空间而不进行回收.除非内存无限大,我们可以任性的分配而不回收,但是事实并非如此.所以,垃圾回收是必须的. 二. ...

  8. 深入理解JVM(三)——JVM之判断对象是否存活(引用计数算法、可达性分析算法,最终判定),Eclipse设置GC日志输出,引用

    本文转载自https://blog.csdn.net/ochangwen/article/details/51406779 本文是基于周志明的<深入理解Java虚拟机> 堆中几乎存放着Ja ...

  9. JVM——引用计数算法与可达性分析算法

    前几篇博客我们一起认识了JVM的内存模型(程序计数器.虚拟机栈.本地方法栈.方法区与堆),了解了它们的内存结构与分配,同时也略带提到关于内存的回收. JVM--内存模型(一):程序计数器 JVM--内 ...

最新文章

  1. MSRA副院长周明博士:四大研究领域揭示自然语言技术的奥秘
  2. hive的row_number()、rank()和dense_rank()的区别以及具体使用
  3. 计算机 旧词新说_如何使旧计算机再次有用
  4. 云小课 | 不了解EIP带宽计费规则?看这里!
  5. centos mysql 升级 5.7_CentOS 7下升级MySQL5.7.23的一个坑
  6. Python内置函数ord()与chr()
  7. 单结晶体管的导电特性_二极管的导电特性
  8. 基于深度学习的大豆病虫害自动计数(SLIC超像素方法进行图像分割)
  9. NLP和CV的双子星,注入Mask的预训练模型BERT和MAE
  10. html 伪元素作用,:before,:after伪元素妙用
  11. 通信原理及系统系列3—— 基于卷积编码的2ASK通信系统设计(非相干解调)
  12. PHPstudy使用教程
  13. 大神详解开源 BUFF 增益攻略丨直播
  14. ubuntu Rhythmbox 乱码解决
  15. Python学习笔记-北京图灵学院-Python概述-20200525
  16. Rust语言正在兴起,Java、Python、C的末日来临?
  17. 2023年湖北武汉中级工程师怎么申请?申报渠道有哪些?启程别
  18. 任务管理器测试内存泄露
  19. 有哪些有格调的免费素材网站?
  20. 智能物联网网关有哪些必备功能

热门文章

  1. 浏览器提示(KB927917)解决方案
  2. win10卸载Ubuntu14:Mbrfix方法卸载失败
  3. qt窗口置顶linux环境下,Qt 实现窗口置顶与取消置顶
  4. Onvif协议之gSOAP工具
  5. 组态王 6.55 启停plc_6个经典的PLC程序实例,学会不求人
  6. [AI教程]TensorFlow入门:手势数字识别
  7. php 设置针式打印机纸张,针式打印机设置,详细教您针式打印机怎么设置纸张大小。...
  8. transparent透明
  9. js进阶 发送短信案例 自动跳转页面
  10. Linux 系统下Oracle数据库自动备份