Сбор мусора объектов, которые отслеживают свои собственные экземпляры на внутренней карте - PullRequest
2 голосов
/ 06 декабря 2008

В конструкторе моего класса я отображаю текущий объект ( this ) вместе с его ключом (строка, введенная в качестве параметра в конструкторе) в статический LinkedHashMap, чтобы я мог ссылаться на объект по строке везде, где это может понадобиться позже.

Вот код (если он помогает):

public class DataEntry {
    /** Internal global list of DataEntry objects. */
    private static LinkedHashMap _INTERNAL_LIST;

    /** The data entry's name. */
    private String NAME;

    /** The value this data entry represents. */
    private Object VALUE;


    /** Defines a DataEntry object with a name and a value. */
    public DataEntry( String name, Object value )
    {
        if( _INTERNAL_LIST == null )
        {
            _INTERNAL_LIST = new LinkedHashMap();
        }

        _INTERNAL_LIST.put( name, this );

        NAME = name;
        VALUE = value;
    }
}

Проблема? Экземпляры этого класса не будут собирать мусор, когда я их использую.

Мне просто любопытно, есть ли способ очистить экземпляры этого класса, когда я их использую, без необходимости каждый раз вручную вызывать метод Remove () или что-то еще (чтобы удалить его ссылку во внутреннем LinkedHashMap, когда я больше не использую их, я имею в виду).

Ответы [ 3 ]

6 голосов
/ 07 декабря 2008

Задайте значения WeakReferences (или SoftReferences ). Таким образом, значения все еще можно собирать. Конечно, у вас все еще будут записи на карте, но вы можете периодически очищать карту от любых записей, в которых Слабая / Мягкая ссылка теперь пуста.

5 голосов
/ 07 декабря 2008

Создание объекта видимым для других до завершения его конструктора не является потокобезопасным.

Не ясно, как карта используется в этом случае, но предположим, что в классе есть такой статический метод, как этот:

public static DataEntry getEntry(String name) {
  return _INTERNAL_LIST.get(name);
}

Другой поток, работающий одновременно, может получить доступ к DataEntry во время его создания и начать использовать запись с неинициализированным VALUE. Даже если вы переупорядочите код в конструкторе так, чтобы добавление нового экземпляра на карту было последним, что вы делаете, JVM разрешается переупорядочивать инструкции, чтобы объект был добавлен в список первым. Или, если класс расширен, инициализация подкласса может иметь место после публикации объекта.

Если более одного потока получает доступ к взаимодействиям с классом DataEntry, вы можете столкнуться с ошибкой параллелизма, которая зависит от платформы, периодически и очень сложно диагностировать.

Статья Брайана Гетца «Безопасное строительство» содержит больше информации по этой теме.

Возвращаясь к первоначальному вопросу: использование WeakReference, как уже упоминалось другими, является хорошим подходом, но вместо того, чтобы перебирать каждую запись на карте, я бы рекомендовал создать оболочку для ваших значений, которая расширяет WeakReference (это может быть ваш DataEntry сам или помощник) и помещать в очередь каждую ссылку в ReferenceQueue. Таким образом, вы можете быстро опросить очередь на предмет любых собранных записей и удалить их с карты. Это может быть выполнено фоновым потоком (блокировка remove), запущенным в инициализаторе класса, или любые устаревшие записи могут быть очищены ( опросом ) каждый раз, когда новая запись добавлено.

Если ваша программа многопоточная, вы должны отказаться от LinkedHashMap для карты с java.util.concurrent или обернуть LinkedHashMap с Collections.synchronizedMap().

1 голос
/ 07 декабря 2008

То, что вы хотите использовать, кажется слабой ссылкой. Концепция заключается в том, что слабые ссылки не являются достаточно сильными, чтобы заставить объект не быть собранным GC. У меня нет большого опыта с ними, но вы можете узнать больше здесь .

...