当前位置: 代码网 > it编程>编程语言>Java > Java怎样判断堆区中的对象可以被回收了

Java怎样判断堆区中的对象可以被回收了

2024年12月26日 Java 我要评论
如何判断堆区中的对象可以被回收了在java中,垃圾回收机制会帮助我们自动回收不再被使用的对象,已到达即使释放内存的效果,但是java又是怎么知道哪些对象不会再被我们继续使用了呢,希望你通过本篇文章,理

如何判断堆区中的对象可以被回收了

在java中,垃圾回收机制会帮助我们自动回收不再被使用的对象,已到达即使释放内存的效果,但是java又是怎么知道哪些对象不会再被我们继续使用了呢,希望你通过本篇文章,理解引用计数法与可达性分析法的运行方式

垃圾回收机制

在c/c++中,一个对象如果不再使用,就要手动将其释放掉,但是很多程序员在编写程序的时候经常忘记将一些对象回收,从而就导致了内存泄漏

在java中为了简化对象的内存释放,引入了自动的垃圾回收机制,通过垃圾回收器把不再使用的对象完成自动回收,垃圾回收器主要负责堆上的内存回收,那么垃圾回收器又是如何知道哪些对象可以被回收了呢?

在java中,一个对象是否可以被回收,主要是看这个对象是否被引用,如果对象被引用了,说明对象还在使用,是不可以被回收的,比如说如下代码中,堆内存中的demo对象被demo引用,那么堆中的demo对象就不会被回收:

public class demo {
    public static void main(string[] args) {
        demo demo = new demo();
    }
}

若此时将demo的引用设置为null:

demo = null;

那么此时demo对象就处于了没有被引用的状态:

此时demo会被垃圾回收器进行回收,那么垃圾回收器又是怎么知道demo对象目前没有被引用呢???

引用计数法

引用计数法会为每一个对象维护一个引用计数器,当对象被引用时加一,取消引用时减一,在上面的代码中,demo引用了堆上的demo对象,所以demo对象的引用计数器就加一,当把demo赋值为null的时候,也就是取消了demo的引用,此时demo对象引用计数器将减一成为0,此时垃圾回收器就认为demo对象此时没有被任何引用,可以回收

但是此时会出现一个问题,如果我new了两个对象a与b,并且a对象与b对象的互为彼此的成员变量,那么就会出现循环引用的现象,此时a对象被栈内存中的a1引用,也被b对象中的a变量引用,那么他的引用技术器应该为2:

public class demo {
    public static void main(string[] args) {
        a a1 = new a();
        b b1 = new b();
        a1.b = b1;
        b1.a = a1;

    }
}
class a {
    b b;
}
class b {
    a a;
}

那么此时,我取消a1、b1对a与b的引用

a1 = null;
b1 = null;

我们已经无法在程序中获取到a与b对象了,因为他们两个除了彼此间的引用关系,已经没有任何引用能够找到他们,所以按照常理来说,a与b对象都不会在程序中再使用了,理应被垃圾回收器进行回收,但是又由于存在彼此间的引用关系,引用计数器的值并不是0,那么此时垃圾回收器又会认为a与b都存在被引用的关系,所以并不会回收这两个类

那么这种情况显然是不对的,无法回收的对象有可能会导致内存泄漏,所以java并没有使用这种方法来判断类是否应该被回收,而是使用了另外一种方式

可达性分析法

java使用的是可达性分析算法来判断对象是否可以被回收

可达性分析法将对象分为两类:

  • 垃圾回收根对象(gc root)
  • 普通对象

对象与对象之间存在引用关系,形成一个引用链, 可达性分析算法就是指gc root对象到某个对象间是可达的,即从gc root对象开始,通过引用对象可以找到的对象爱国,即认为该对象还不能被回收

此时b、c、d对象都可以通过引用被gc root对象找到,即他们都是可达的,所以不会被视为可回收的对象,但是如果对象a与对象b之间取消引用关系,那么即使对象c与对象d任然存在引用关系,但他们是不可达的,因此他们会被回收

java虚拟机会持有一个所有gc root对象的列表,用来判断哪些对象是不可达的,不可达的对象将被垃圾回收器进行回收:

再次回看上面的案例,如果使用可达性分析法,那么堆内存中应该存在一个gc root对象引用了主线程里面mian方法的栈帧中的对象,此时如果a1与b1不再引用堆中的对象,那么就算a对象与b对象存在引用关系,但是他们是不可达的,就会被视为等待回收的对象:

那么哪些对象可以被当中gc root对象呢?

主要有四种gc root对象:

  • 1.线程thread对象
  • 引用线程栈帧中的方法、参数、局部变量等等,上面我们的案例中就是线程thread对象引用了mian方法栈帧中的a1与b1
  • 2.类加载器加载到的java.lang.class对象
  • 引用类中的静态变量
  • 3.监视器对象
  • 用来保存同步锁synchronized关键字持有的对象
  • 4.本地方法方法调用时使用的全局对象
  • 由java虚拟机来控制调用

总结

以上为个人经验,希望能给大家一个参考,也希望大家多多支持代码网。

(0)

相关文章:

版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。

发表评论

验证码:
Copyright © 2017-2025  代码网 保留所有权利. 粤ICP备2024248653号
站长QQ:2386932994 | 联系邮箱:2386932994@qq.com