Java 垃圾收集参考 类

Java Garbage Collecting Reference Classes

我正在测试 gc 如何与 java.lang.ref 包中的 类 一起工作,仅供学习:)

以下是我的代码。

public static void main(String [] args) {

    int mb = 1024*1024;

    //Getting the runtime reference from system
    Runtime runtime = Runtime.getRuntime();

    System.out.println("##### Heap utilization statistics [MB] #####");
    ArrayList<Object> sb = new ArrayList<Object>();
    for(int i =0; i < 5000000; i++){
        sb.add(new Object());
        if( i % 1000 == 0) {
            System.out.println(i);
        }
    }

    SoftReference<ArrayList> wr = new SoftReference<ArrayList>(sb);

    // System.gc()

    //Print used memory
    System.out.println("Used Memory:"
        + (runtime.totalMemory() - runtime.freeMemory()) / mb);

    //Print free memory
    System.out.println("Free Memory:"
        + runtime.freeMemory() / mb);

    //Print total available memory
    System.out.println("Total Memory:" + runtime.totalMemory() / mb);

    //Print Maximum available memory
    System.out.println("Max Memory:" + runtime.maxMemory() / mb);
}

结果:

Used Memory:95,
Free Memory:28,
Total Memory:123,
Max Memory:247

然后我取消评论"System.gc()",重新运行代码,结果是

Used Memory:1,
Free Memory:122,
Total Memory:123,
Max Memory:247

是的,首先收集了ArrayList的实例。据我所知,仅由 SoftReference 引用的 Instances 是 softreachable,因此在真正需要 GC 时收集,因为缺少左堆 space。代码第一个结果的左边 space 大约是 150(空闲内存 28 + 左边最大内存 124)。不明白为什么要收集ArrayList的实例

其次,我运行修改了代码:

sb.add(new Object()); -> sb.add(new StringBuffer(i));

结果:

Used Memory:245,
Free Memory:2,
Total Memory:247,
Max Memory:247

为什么不一样?

最后,我 运行 再次修改代码: 来自

SoftReference<ArrayList> wr = new SoftReference<ArrayList>(sb);

WeakReference<ArrayList> wr = new WeakReference<ArrayList>(sb);

结果:

Used Memory:245,
Free Memory:2,
Total Memory:247,
Max Memory:247

我猜想收集了 ArrayList 的实例,因为这些实例仅被 WeakReferece 引用,所以这些实例是弱可达的。但是没有收集到。

我现在假设我对 Reference 工作方式的理解是错误的。

请任何人告诉我原因。

谢谢^^

嵌入式问题最容易回答:如果您将 new Object() 替换为 new StringBuffer(i) 并增加 i,您将创建容量增加的 StringBuffer 个实例,因此,这些对象比无状态 Object 实例需要更多内存也就不足为奇了。

主要问题不太容易回答,因为您向我们展示了一个难以重现的结果,而在此期间转向了一个更容易重现的结果,这表明您已经改变了更多在您的代码中比您所说的要多,或者您的测试环境之间有细微的变化。原则上,这两种结果都是可能的,但与您所做的更改完全无关。

首先,当你调用 System.gc() 时,你持有对局部变量中 ArrayList 的强引用,所以无论附加引用是弱引用还是软引用,都完全无关紧要。在大多数设置和测试运行中,您会遇到 ArrayList 和包含的对象仍然占用内存。

但这并不是故事的结局。正如 “finalize() called on strongly reachable object in Java 8” 中所讨论的,如果 JVM 可以证明不会使用该引用,那么即使持有强引用,对象 也可以 被收集。正如进一步讨论的那样,这是否会发生仅取决于 JVM 的优化状态和执行的代码,因此您的示例程序不太可能发生,该示例程序由通常在解释器中运行的唯一 main 方法组成,但是也不是不可能。

但如果发生这种情况,此逻辑适用于您的方法中 所有 未使用的引用,其中包括对 SoftReference 的引用。 WeakReference 实例。如果那个 Reference 对象本身被收集,它与所指对象的语义将再次无关紧要。然后,将ArrayList、包含对象和引用对象收集到一起。

如果在调用 System.gc() 之前显式地将 sb 变量设置为 null 并在它之后对引用对象调用 get(),您可能会遇到不同的结果,但是请记住,System.gc() 仍然只是对 JVM 的 提示 ,可能会被忽略,因此根本没有任何效果。