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虚拟机来控制调用
总结
以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。
相关文章
解决nacos启动报错Server check fail, please che
这篇文章主要介绍了nacos启动 Server check fail, please check server localhost ,port 9848 is available的错误原因以及解决方法,需要的朋友可以参考下2023-09-09深入学习Spring Boot排查 @Transactional 引起的 NullPointerException问题
这篇文章主要介绍了深入学习Spring Boot排查 @Transactional 引起的 NullPointerException问题,需要的朋友可以参考下2018-01-01
最新评论