Java垃圾收集

示例

C ++方法-新增和删除

在像C ++这样的语言中,应用程序负责管理动态分配的内存所使用的内存。当使用new运算符在C ++堆中创建对象时,需要相应地使用delete运算符来处置该对象:

  • 如果程序忘记了delete一个对象而只是“忘记”了该对象,则关联的内存将丢失给应用程序。这种情况的术语是内存泄漏,它会导致过多的内存泄漏,因此应用程序可能会使用越来越多的内存,并最终导致崩溃。

  • 另一方面,如果应用程序尝试delete两次访问同一对象,或者在删除对象后使用该对象,则该应用程序很可能由于内存损坏问题而崩溃。

在复杂的C ++程序中,使用new和实现内存管理delete可能很耗时。确实,内存管理是错误的常见来源。

Java方法-垃圾收集

Java采用了不同的方法。deleteJava提供了一种称为垃圾回收的自动机制来回收不再需要的对象使用的内存,而不是显式的运算符。Java运行时系统负责查找要处置的对象。此任务由称为垃圾收集器(简称GC)的组件执行。

在执行Java程序的任何时候,我们都可以将所有现有对象的集合分为两个不同的子集1

  • JLS定义可到达的对象,如下所示:

    可达对象是可以从任何活动线程进行任何潜在的连续计算中访问的任何对象。

    实际上,这意味着从范围内局部变量或static某些代码可能能够到达该对象的变量开始,存在一系列引用。

  • 无法访问的对象是无法如上所述实现的对象。

这是不可达的任何对象都是有资格进行垃圾回收。这并不意味着它们被垃圾收集。事实上:

  • 无法到达的对象不会在无法到达时立即被收集1

  • 无法访问的对象可能永远不会被垃圾回收。

Java语言规范为JVM实现提供了很大的自由度,以决定何时收集无法访问的对象。在实践中,它还允许JVM实现保守地检测到无法访问的对象。

JLS保证的一件事是,将永远不会收集任何可到达的对象。

当对象变得不可访问时会发生什么

首先,当对象变得不可访问时,没有什么特别的事情发生。只有当垃圾收集器运行并且它检测到对象不可访问时,事情才会发生。此外,GC运行通常不会检测所有无法访问的对象。

当GC检测到无法访问的对象时,可能会发生以下事件。

  1. 如果有任何Reference引用该对象的对象,则将在删除该对象之前清除这些引用。

  2. 如果对象是可终结的,则它将被终结。这是在删除对象之前发生的。

  3. 可以删除该对象,并可以回收该对象占用的内存。

请注意,上面的事件可以按明确的顺序发生,但是不需要垃圾回收器在任何特定时间范围内对任何特定对象执行最终删除。

可达对象和不可达对象的示例

考虑以下示例类:

// A node in simple "open" linked-list.
public class Node {
    private static int counter = 0;

    public int nodeNumber = ++counter;
    public Node next;
}

public class ListTest {
    public static void main(String[] args) {
        test();                    // M1
        System.out.prinln("Done"); // M2
    }
    
    private static void test() {
        Node n1 = new Node();      // T1
        Node n2 = new Node();      // T2
        Node n3 = new Node();      // T3
        n1.next = n2;              // T4
        n2 = null;                 // T5
        n3 = null;                 // T6
    }
}

让我们检查一下当test()被调用时会发生什么。语句T1,T2和T3创建Node对象,以及对象是所有通过可到达n1,n2以及n3分别变量。语句T4将对第二个Node对象的引用分配给next第一个对象的字段。完成此操作后,Node可通过两条路径到达第二个:

 n2 -> Node2
 n1 -> Node1, Node1.next -> Node2

在语句T5中,我们将分配null给n2。这打破了的第一个可达性链Node2,但第二个保持不间断,因此Node2仍然可以实现。

在语句T6中,我们将分配null给n3。这打破了唯一的可达性链Node3,从而使它Node3无法实现。但是,Node1和Node2仍然可以通过n1变量访问。

最后,当test()方法返回时,它的局部变量n1,n2而n3走出去的范围,因此不能被任何东西所访问。这破坏了剩余的可达性链条Node1和Node2,和所有的Node对象也不可达,有资格进行垃圾回收。


1-这是一种简化,忽略了终结处理和Reference类。2-假设,Java实现可以做到这一点,但是这样做的性能代价使其不切实际。