为什么我的变量不超出范围?

时间:2012-02-25 01:23:17

标签: java garbage-collection jvm

下午好,

我被告知当函数返回时,变量(在该函数范围内)会自动超出范围,因此我们不必将它们设置为null。

然而,这似乎并非如此。

我有一个测试代码,用于创建指向java.lang.ref.PhantomReference实例的java.lang.Object。对该对象的唯一强引用是在函数F的范围内。

换句话说,当该函数返回时,不应再对该对象有任何强引用,并且该对象现在应该可由GC收集。

然而,无论我如何努力使内存的JVM挨饿,GC都会拒绝收集该对象。令人惊讶的是,如果我将变量设置为null(obj = null;),GC现在会收集该对象。

这种古怪背后的解释是什么?

public class Test {
    public static void main(String args[]) {
        // currently testing on a 64-bit HotSpot Server VM, but the other JVMs should probably have the same behavior for this use case
        Test test = new Test();
        test.F(new Object());
    }

    public <T> void F(T obj) {
        java.lang.ref.ReferenceQueue<T> ref_queue = new java.lang.ref.ReferenceQueue<T>();
        java.lang.ref.PhantomReference<T> ref = new java.lang.ref.PhantomReference<T>(obj, ref_queue); // if this line isn't an assignment, the GC wouldn't collect the object no matter how hard I force it to 
        obj = null; // if this line is removed, the GC wouldn't collect the object no matter how hard I force it to
        StartPollingRef(ref_queue);
        GoOom();
    }

    private <T> void StartPollingRef(final java.lang.ref.ReferenceQueue<T> ref_queue) {
        new java.lang.Thread(new java.lang.Runnable() {
            @Override
            public void run() {
                System.out.println("Removing..");
                boolean removed = false;
                while (!removed) {
                    try {
                        ref_queue.remove();
                        removed = true;
                        System.out.println("Removed.");
                    } catch (InterruptedException e) { // ignore
                    }
                }
            }
        }).start();
    }

    private void GoOom() {
        try {
            int len = (int) java.lang.Math.min(java.lang.Integer.MAX_VALUE, Runtime.getRuntime().maxMemory());
            Object[] arr = new Object[len];
        } catch (Throwable e) {
            // System.out.println(e);
        }
    }
}

1 个答案:

答案 0 :(得分:9)

符合标准的JVM永远不会义务来收集内存。也就是说,你不能编写一个程序,其正确性取决于在特定时间收集的特定内存位:你既不能强迫JVM收集(甚至通过System.gc()!)也不能依赖它来这样做

所以,你所观察到的行为在定义上不能是错误的:你有目的地试图让环境做一些没有责任的事情。

大家都这么说,你的问题是你的对象没有超出范围。它在main中创建,然后以正常的Java引用方式传递给F。在F返回之前,T obj名称仍然是对您的对象的引用。

goOom设为静态并在main中调用它,您应该会看到该对象被收集。但是,再说一遍,你可能仍然没有,那就不会错误 ......