Java垃圾收集引用类

时间:2016-04-23 02:35:29

标签: java memory garbage-collection jvm heap

我正在测试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引用的实例是softreachable,因为缺少剩余堆空间而在真正需要GC时收集。代码的第一个结果的左侧空间大约是150(自由mem 28 + left max mem 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引用,因此这些实例是弱可达的。但他们没有收集。

我现在假设我对参考工作方式的理解不正确。

请有人告诉我原因。

Thx ^^

1 个答案:

答案 0 :(得分:2)

嵌入式问题最容易回答:如果您将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 resp的引用。 WeakReference个实例。如果该Reference对象本身被收集,那么它对指示对象的语义将再次无关紧要。然后,ArrayList,包含的对象和参考对象被收集在一起。

如果在调用sb之前明确将null变量设置为System.gc()并在之后的引用对象上调用get(),则可能会遇到不同的结果,但请保留请注意,System.gc()仍然只是JVM的提示,可能会被忽略,因此根本没有效果。