理解JAVA的垃圾回收机制

当JAVA垃圾收集器(GC)运行时,它作为一个守护线程在后台运行,用于为用户线程提供服务或执行JVM任务 。它定期检查堆内存中的所有对象 , 并识别那些不再被程序的任何部分引用的对象(也就是不再被引用的对象) 。然后 , 这些不再被引用的对象将被销毁,并释放空间供新创建的对象使用 。
以下是Java垃圾收集过程的简单步骤:
1.标记(Mark):从GC根开始,识别当前正在使用和未使用的对象引用 , 未使用的对象被标记为垃圾 。2.清除(Sweep):遍历堆,并找到存活对象之间的未使用空间,将这些空间记录在一个空闲列表中,供将来的对象分配使用 。3.压缩(Compact):将所有存活的对象移动到一个连续的内存区域中 , 以提高新对象的内存分配性能 。
然而 , 这种方法存在一些问题:
•效率低下,因为大多数新创建的对象很快就会变得无用 。•长寿命对象很可能在将来的GC周期中仍然被使用 。
为了解决这些问题,实际上,新创建的对象会根据其存活时间存储在堆的不同代空间中 。然后 , 在执行完整的垃圾收集之前,垃圾收集会在两个主要阶段进行,称为Minor GC(年轻代垃圾收集)和Major GC(老年代垃圾收集),对象会在这些代之间进行扫描和移动 。下图展示了堆内存的这种划分:
JVM堆内存


理解JAVA的垃圾回收机制

文章插图
标记-清除模型标记-清除模型是Java垃圾收集的基本实现 。它有两个主要阶段:
1.标记(Mark):从GC根开始,识别并标记所有仍然被引用的对象,其余的被认为是垃圾 。2.清除(Sweep):遍历堆,并找到存活对象之间的未使用空间,将这些空间记录在一个空闲列表中 , 供将来的对象分配使用 。
Java垃圾收集的根现在你知道了,当没有引用指向一个对象时,它变得不可访问,因此也成为垃圾收集的候选对象 。等等,这是什么意思?引用是指什么?那么第一个引用是什么?我最初也有同样的问题 。让我解释一下这些引用和可达性在底层是如何发生的 。
为了让你的应用程序代码能够访问一个对象,必须存在一个根对象,它与你的对象相连,并且能够从堆外部访问 。这些从堆外部可以访问的根对象称为垃圾收集(GC)根 。垃圾收集根有几种类型,比如局部变量、活动的Java线程、静态变量、JNI引用等(只是了解这里的思想 , 如果你进行快速的谷歌搜索,可能会找到许多关于GC根的不一致的分类) 。我们需要学习的是,只要我们的对象被这些GC根之一直接或间接引用,并且GC根保持活动状态,我们的对象就可以被认为是可达的 。一旦我们的对象失去与GC根的引用 , 它就变得不可达,因此可以进行垃圾收集 。

理解JAVA的垃圾回收机制

文章插图
Image.png
可达性与垃圾收集的资格垃圾收集器只会销毁不可达的对象 。它是在后台自动进行的过程,一般情况下 , 程序员不需要对此做任何操作 。
注意:在销毁对象之前,垃圾收集器最多会在该对象上调用一次finalize()方法(finalize()方法不会对任何给定的对象多次调用) 。默认的finalize()方法为空实现 。通过重写它,我们可以执行一些清理活动 , 比如关闭数据库连接或验证对象的结束,就像我下面写的那样 。一旦finalize()方法完成,垃圾收集器将销毁该对象 。
考虑下面的Person类 , 它有一个对象构造函数和finalize()方法:
class Person {// 存储人员(对象)的名称String name;public Person(String name) {this.name = name;}@Override/* 重写finalize方法,以检查哪个对象被垃圾收集 */protected void finalize() throws Throwable {// 将打印出人员(对象)的名称System.out.println("Person对象 - " + this.name + " -> 成功被垃圾收集");}}如果满足以下任一情况,对象可以立即变为不可达(无需等待堆中的分代老化) 。
情况1:将引用变量置为null当一个对象的引用变量被改为null时 , 该对象变得不可达,从而可以进行垃圾收集 。
// 创建一个Person对象


推荐阅读