java垃圾回收机制——对象之死

如何断定对象已死

1、引用计数法

在对象内部设置一个引用计数器,当有引用指向该对象时,计数器加一,引用失效则计数器减一。当计数器的值为0时,代表该对象没有被任何引用指向,此时对象就可以被回收了。
这是一种简单且易实现的判断对象是否存活的方式,但也有着很大的缺陷。举个栗子,假设有如下类定义:

public class Demo {    
    public Demo refer;
}

再写一段测试代码如下:

Demo a = new Demo();
Demo b = new Demo();
a.refer = b;
b.refer = a;
// 这时不再需要a对象和b对象了,下面两行代码将a和b的引用无效掉,希望这两个对象能被回收。
a = null;
b = null;

这时候根据引用计数的方式能够将这两个对象回收掉吗,答案是NO,而问题的根源在于
a.refer = b;
b.refer = a;
因为这两行代码的存在导致了即使a和b的引用被无效掉了,但仍存在引用还指向这两个对象,所以对象仍然苟活了下来。情况大概如下图所示:
在这里插入图片描述

2、可达性分析

可达性分析解决了引用计数法中因为对象内部相互引用而无法被回收的问题。
jvm通过一系列称为“GC Roots”的根对象作为起始节点集,从这些节点开始,根据引用关系向下搜索,搜索过程所走过的路径称为“引用链”(Reference Chain),如果一个对象没有存在于任何一条引用链上,则说明该对象应该被回收了。如图所示:
在这里插入图片描述
看到这我们不禁会想什么对象应该属于“GC Roots”中的一员呢,因为情况实在很多,我就直接复制粘贴周志明大佬书中内容了:
1、在虚拟机栈(栈帧中的本地变量表)中引用的对象,譬如各个线程被调用的方法堆栈中使用到的参数、局部变量、临时变量等。
2、在方法区中类静态属性引用的对象,譬如Java类的引用类型静态变量。
3、在方法区中常量引用的对象,譬如字符串常量池(String Table)里的引用。
4、在本地方法栈中JNI(即通常所说的Native方法)引用的对象。
5、Java虚拟机内部的引用,如基本数据类型对应的Class对象,一些常驻的异常对象(比如NullPointExcepiton、OutOfMemoryError)等,还有系统类加载器。
6、所有被同步锁(synchronized关键字)持有的对象。
7、反映Java虚拟机内部情况的JMXBean、JVMTI中注册的回调、本地代码缓存等。

这时让我们回到引用计数法中所举的例子,请问图中情况在真正的java代码中两个对象最后会被回收吗,这次的答案是YES,因为主流的jvm就是采用的可达性分析算法。
那么让我们仔细盘一盘上面代码中出现的所有引用,显然,代码中一共出现了四个引用:a 、b 、a.refer 、b.refer。
a和b完美符合上面GC Roots七种分类中的第一类,它们是两个局部变量,根正苗红,属于GC roots。
而a.refer 、b.refer想方设法能沾上边的就是第二类,可惜输在了不是静态,所以不会被算作是GC Roots。
最终,虽然两个对象还被各自的refer引用互相连接着,但这两个引用不属于GC Roots的一员,所以最后还是会惨被回收。

finalize()与对象的自我救赎

如果一个对象经可达性分析后没有与任何一个引用链相连,对象如果重写了finalize()方法且该方法还没有被执行过,那么对象会被加入一个名为F-Queue的队列,jvm会创建一个低优先级的线程去执行队列中对象的finalize()方法,但jvm并不保证一定会等待该方法执行完,所以一般情况下并不鼓励去重写finalize()方法去实现某个重要的功能
如果对象通过finalize()方法将自己和引用链上的对象再次相连,便可以逃出生天,不过机会只有一次,因为对象的finalize()方法被执行过了就不会再执行第二次了。之后该对象又要被回收的话,那么它会直接嗝屁了。为了证明这一点。测试代码如下:

public class SurvivorDemo {
    static SurvivorDemo instance;

    public static void main(String[] args) {
        instance = new SurvivorDemo();
        instance = null;
        System.out.println("first gc");
        System.gc();
        if(instance != null){
            System.out.println("instance is alive!");
        }else{
            System.out.println("instance is dead!");
        }
        instance = null;
        System.out.println("second gc");
        System.gc();
        if(instance != null){
            System.out.println("instance is alive!");
        }else{
            System.out.println("instance is dead!");
        }
    }

    @Override
    protected void finalize() throws Throwable {
        super.finalize();
        System.out.println("我不会就这样轻易的go die!");
        instance = this;
    }
}

输出如图:
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值