对象能否被垃圾回收的判断方式

本文深入探讨了JVM如何判断对象是否可回收,重点介绍了引用计数法和可达性分析两种方法。引用计数法简单直观,但无法处理循环引用问题;可达性分析则从GC Roots出发,标记所有可达对象,未被标记的对象被视为垃圾。同时,文章讨论了多线程环境下产生的误报和漏报问题,以及Java虚拟机通过stop-the-world机制解决这些问题。

对于垃圾回收,JVM 是如何判断是否已经死亡了或者无需使用,而回收空间呢?主要有两种方法

引用计数法

引用计数法就是对于对象,如果有变量引用它,则对于这个对象的引用计数加一。如果在引用的计数为 0 的时候,则该变量则是已死亡,可以回收了。

实际流程这样的,有一个引用,被赋值某一对象,那么该对象的引用计数器加一。一个引用指向某一对象,如果修改这个引用的指向,那么将该对象的引用计数减一。

所以要获取全部的引用更新操作。相应增减目标对象引用计数器。

  • 缺点
    • 需要额外的空间存储计数器
    • 繁琐的更新
    • 无法处理循环对象
      • a,b 相互引用,除此没有指向 a,b 的情况,a,b 实际已经死了。但是引用计数器不为 0。内存泄露

可达性分析

主流的采取的是可达性分析。

实质将一系列 GC Roots 作为初始存活对象合集,从该集合出发。探索能够到达的被该集合引用的对象,标记(mark),并将其加入该集合中。最终未被探索到的被定义为死亡,可以被回收。

GC Roots

简单的理解为是堆外指向对内的引用。一般包含如下几种

  • Java 方法栈帧中的局部变量
  • 已加载类的静态变量
  • JNI handles
  • 已经启动未停止的 Java 线程

但是这里又有问题,在多线程的环境下,会产生误报(把引用设置为 null,标记好后,其它线程产生了垃圾,即将活的变死了,这种内存是不会释放的),或者漏报(如果指向一个新的对象,这个对象可没有被标记为不能回收,垃圾回收器就直接给回收掉了)。就麻烦了。

面对这种问题,Java 虚拟机提出的方式是 stop-the-world

stop-the-word 及安全点

也就是,停止其他非垃圾回收的线程工做,直至完成垃圾回收。也就是垃圾回收的暂停时间(GC pause)

这种是通过安全点机制实现的。当 Java 请求垃圾回收(stw)时候,会等待所有线程达到安全点,才也许 stw 线程独占工作。

这里安全点并非是让其他线程停下,而是找到一个稳定的执行的环境状态。在该状态下堆栈不发生变化。

例如本地方法,Java 程序执行 JNI 执行本地代码,如果这段代码没有引用对象、调用 Java 方法或访问 Java 原方法。那么堆栈结构就不会发生变化,那么就是一个安全点。

只要不离开安全点,虚拟机回收垃圾就能和运行本地代码同时执行。还要其他,例如

  • 解释执行字节码
  • 执行即使编译器生成的机器码
  • 线程阻塞
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值