Объекты сбора мусора, которые отслеживают свои собственные экземпляры на внутренней карте

В конструкторе своего класса я сопоставляю текущий объект (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, когда я Я имею ввиду).


person Daddy Warbox    schedule 06.12.2008    source источник
comment
Должен ли класс работать в средах, использующих потоки? В настоящее время не будет.   -  person McDowell    schedule 07.12.2008
comment
Что конкретно его останавливает?   -  person Daddy Warbox    schedule 07.12.2008
comment
Статическая переменная _INTERNAL_LIST не синхронизирована. Есть вероятность, что несколько потоков вызовут инициализацию нескольких карт. Кроме того, LinkedHashMap не синхронизируется, поэтому синхронные вызовы put могут оставить карту в нестабильном состоянии.   -  person McDowell    schedule 07.12.2008
comment
Собственно, я не могу представить себе ситуацию, когда кеширование через конструктор было бы хорошей идеей.   -  person McDowell    schedule 07.12.2008
comment
Статика плохая. Показывать это в конструкторе - плохо. Не использовать final в этих полях - это плохо. Использование нечетных соглашений об именах - это плохо. Делать все вышеперечисленное действительно плохо.   -  person Tom Hawtin - tackline    schedule 07.12.2008
comment
Хорошая практика для меня не означает приседания, если она не дает того, что мне нужно. В конце концов я попытаюсь перейти на более изящное высокоуровневое решение для всего этого, когда смогу решить свои основные проблемы.   -  person Daddy Warbox    schedule 07.12.2008


Ответы (3)


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

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

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

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

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

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

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

Если ваша программа многопоточная, вам следует отказаться от LinkedHashMap для карты из _ 11_ или оберните LinkedHashMap _ 13_.

person erickson    schedule 06.12.2008
comment
Спасибо за дополнительную информацию. Многопоточность - это мост, который мне, возможно, придется пересечь, но пока я просто неуклюже разбираюсь в основах. В любом случае я рассмотрю этот альтернативный подход. - person Daddy Warbox; 07.12.2008

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

person Jon Skeet    schedule 06.12.2008
comment
Блин, ты меня обыграл до слабых ссылок :) - person Morten Christiansen; 07.12.2008
comment
Был занят редактированием - WeakHashMap на самом деле не подходит, так как в конечном итоге оказываются слабыми ключи, а не значения. Фактически вам придется написать что-то похожее, но, вероятно, оно не должно быть таким мощным, если вы используете его только для одной конкретной ситуации. - person Jon Skeet; 07.12.2008
comment
Хорошо спасибо. Да, я этого боялся. Я, вероятно, просто буду использовать методы Remove и буду делать это ленивым способом. :П - person Daddy Warbox; 07.12.2008

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

person Morten Christiansen    schedule 06.12.2008