Объект со слабой ссылкой не будет собирать мусор - PullRequest
1 голос
/ 19 января 2020

Меня беспокоит экземпляр объекта, на который когда-то были жесткие ссылки, но после явного нулевого присваивания его сильной ссылке и после явного вызова System.gc() экземпляр все еще доступен через слабую ссылку . Если я правильно понимаю, когда у упомянутого объекта остаются только слабые ссылки, референт гарантированно будет очищен в следующем G C сеансе. Чего мне не хватает?

Ссылочный код:

public class References {
    public static void main(String[] args) {

        Example strongReferenceWrappedInWeak = new Example(42);
        strongReferenceWrappedInWeak.printA();

        WeakReference<Example> exampleWeakReference = new WeakReference<>(strongReferenceWrappedInWeak);

        System.gc();

        Example retrievedExample = exampleWeakReference.get();
        retrievedExample.printA(); //this works, because a strong reference is present to the instance, hence it's not cleared

        strongReferenceWrappedInWeak = null; //eligible for garbage collection

        System.gc();

        Example retrievedExampleTwo = exampleWeakReference.get(); //should be null
        retrievedExampleTwo.printA(); //should throw NPE
    }
}

class Example {
    private int a;

    Example(int a) {
        this.a = a;
    }

    void printA() {
        System.out.println(this.a);
    }
}

Ответы [ 2 ]

4 голосов
/ 19 января 2020

Сборка мусора работает таинственными способами.

В экосистеме Java было несколько реализаций сборщиков мусора с очень разным поведением.

Время выполнения сборки мусора зависит от реализации сборщика мусора, а также может зависеть от текущего состояния JVM. Один сборщик может работать почти непрерывно, в то время как другой может подождать, пока не останется мало памяти. (Я сильно упрощаю здесь, чтобы прояснить суть.)

То, будет ли собран весь мусор или только его часть, может также зависеть от реализации сборщика и состояния JVM.

Вызов System.gc - это просто предложение , а не команда. Сборщик мусора может игнорировать его.

В Java вы не должны прилагать больших усилий для управления памятью. Современные реализации JVM намного лучше, чем любой отдельный программист. Просто убедитесь, что выполнили все ссылки на ваши объекты, когда закончили их использовать. Или используйте WeakReference / SoftReference. Затем доверьте выполнение своей работы JVM и сборщику мусора.

В экстремальных случаях (очень большой объем памяти или экстремальные объемы оттока объектов) вы можете изучить поведение различных реализаций сборщика мусора. И, возможно, рассмотрите альтернативы, такие как Zing от Azul Systems или GraalVM от Oracle. Но для большинства проектов обычные JVM на основе OpenJDK работают достаточно хорошо.

3 голосов
/ 19 января 2020

strongReferenceWrappedInWeak = null не делает экземпляр объекта Example пригодным для сбора мусора, поскольку retrievedExample по-прежнему поддерживает строгую ссылку на него .

Чтобы исправить, добавьте retrievedExample = null;

Example strongReferenceWrappedInWeak = new Example(42);
strongReferenceWrappedInWeak.printA();

WeakReference<Example> exampleWeakReference = new WeakReference<>(strongReferenceWrappedInWeak);

System.gc();

Example retrievedExample = exampleWeakReference.get();
retrievedExample.printA(); //this works, because a strong reference is present to the instance, hence it's not cleared

retrievedExample = null;
strongReferenceWrappedInWeak = null; //now eligible for garbage collection

System.gc();

Example retrievedExampleTwo = exampleWeakReference.get(); //will be null
retrievedExampleTwo.printA(); //will throw NPE

В качестве альтернативы, не создавайте сильную ссылку с локальной переменной, просто вызовите метод непосредственно из слабой ссылки. Таким образом, вы случайно не оставите сильную ссылку, как вы это сделали. * (Во время вызова printA() ссылка this является сильной ссылкой, поэтому объект не может быть G C 'd во время вызова ) *

Example strongReferenceWrappedInWeak = new Example(42);
strongReferenceWrappedInWeak.printA();

WeakReference<Example> exampleWeakReference = new WeakReference<>(strongReferenceWrappedInWeak);

System.gc(); //does not collect object, since strong reference still exists

exampleWeakReference.get().printA(); //works

strongReferenceWrappedInWeak = null; //eligible for garbage collection
System.gc(); //collects object, since it is now weakly referenced only

exampleWeakReference.get().printA(); //throws NPE

Выход (от обоих)

42
42
Exception in thread "main" java.lang.NullPointerException
    at Test.main(Test.java:**)

Проверено на Java 13

...