Почему мои переменные не выходят за рамки? - PullRequest
5 голосов
/ 25 февраля 2012

Добрый день всем,

Меня учили, что когда функция возвращается, переменные (в рамках этой функции) автоматически выходят из области видимости, поэтому нам не нужно устанавливать их в 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 Ответ

9 голосов
/ 25 февраля 2012

Соответствующая стандартам JVM никогда не обязана собирать память.То есть вы не можете написать программу, правильность которой зависит от того, какой конкретный бит памяти собирается в определенное время: вы не можете ни заставить JVM собирать (даже через System.gc()!), Ни полагаться на это.

Итак, поведение, которое вы наблюдаете, не может быть определенно неправильным: вы целенаправленно пытаетесь заставить окружающую среду делать то, чего она не может делать.

Все это говорит о том, что выпроблема в том, что ваш объект не вышел из области видимости.Он создается в main, затем передается - обычным способом Java-ссылки - в F.До тех пор, пока не вернется F, имя T obj будет все еще ссылкой на ваш объект.

Сделайте goOom статическим и поместите вызов в него в main, и вы должны увидеть, как объект будет собран.Но, опять же, вы все еще не можете, и это не будет неправильно ...

...