Когда бы вы использовали WeakHashMap или WeakReference?


163

Использование слабых ссылок - это то, что я никогда не видел в реализации, поэтому я пытаюсь выяснить, каков вариант использования для них и как будет работать реализация. Когда вам нужно было использовать WeakHashMapили WeakReferenceи как это было использовано?



Ответы:


96

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

Поскольку предполагается, что кэш изображений не позволяет нам перезагружать изображения, когда нам это абсолютно не нужно, вы быстро поймете, что в кеше всегда должна содержаться ссылка на любое изображение, которое уже находится в памяти. Однако при использовании обычных сильных ссылок эта ссылка сама заставит изображение оставаться в памяти, что требует от вас как-то определить, когда изображение больше не требуется в памяти, и удалить его из кэша, чтобы оно стало пригодным для сбора мусора. Вы вынуждены дублировать поведение сборщика мусора и вручную определить, должен ли объект находиться в памяти.

Понимание слабых ссылок , Итан Николас


43
Разве SoftReferences не будет лучше в этом случае, то есть ссылки, которые собираются только тогда, когда память начинает исчерпываться.
JesperE

Я немного запутался ... допустим, у меня есть кэш SWT-изображений. Образы SWT должны быть УДАЛЕНЫ с помощью метода dispose () для освобождения ресурсов SO. Если я использую WeakHashMap для их хранения, покажите, точно ли GC удалит объект?
marcolopes

2
@marcolopes GC будет использовать финализатор для любого другого объекта. Кажется, что SWT не нравится, когда вы делаете это, поэтому я не думаю, что вы можете управлять ресурсами операционной системы с помощью WeakHashMap.
Джейкоб Кралл

@marcolopes, (я предполагаю, что ваш GC гарантирует вызов для завершения до того, как освободит память.) Если удаление выполняется в финализаторе кэша, все в порядке. Если dispose - это то, что вам нужно вызвать вручную, то либо 1) расширьте класс и поместите dispose в финализатор, либо 2) используйте фантомную ссылку для отслеживания и запуска dispose соответственно. Вариант 2 лучше (избегает ошибок воскрешения и дает возможность запускать dispose в другом потоке), но вариант 1 легче реализовать без вспомогательных классов.
Pacerier


55

WeakReference против SoftReference

Одно из различий, которое нужно прояснить, - это различие между a WeakReferenceи a SoftReference.

В основном WeakReferenceбудет GC-D в JVM с нетерпением, когда объект ссылки не имеет жестких ссылок на него. С SoftReferenceдругой стороны, объект d будет собираться сборщиком мусора до тех пор, пока ему действительно не понадобится освободить память.

Кэш, где значения хранятся внутри WeakReferences, будет довольно бесполезным (в a WeakHashMapэто ключи, на которые слабо ссылаются). SoftReferencesполезны для переноса значений, когда вы хотите реализовать кеш, который может увеличиваться и уменьшаться с доступной памятью.


4
«Кэш, где значения хранятся внутри WeakReferences, был бы довольно бесполезен», - я совершенно не согласен.
Томас Эдинг

5
@ Trinithis - эм, я действительно не знаю, что сказать. Почему кэш, значение которого исчезает в тот момент , вы не ссылающиеся им полезную вещь , точно?
oxbow_lakes

4
Для чего-то похожего на запоминание может быть полезен кэш, который свободно хранит свои кэшированные значения.
Томас Эдинг

5
@ThomasEding Я до сих пор не понимаю. Единственный случай, когда кэш кажется полезным, это когда нет других ссылок на него ... Если у вас есть ссылки на него, для чего вам нужен кэш?
Cruncher

2
@ThomasEding, Softref сообщает среде «сохраняйте это, пока у вас нет памяти». Weakref сообщает окружению «сохраняйте это до запуска GC». Откровенно говоря, нет никакого варианта использования слабой ссылки, если вы не отлаживаете и не профилируете сам GC. Если вы хотите кэш, чувствительный к памяти, используйте softref. Если вам не нужен кеш, не кешируйте его! Где слабый рефрен?
Pacerier

30

В частности, одним из распространенных применений WeakReferences и WeakHashMaps является добавление свойств к объектам. Иногда вы хотите добавить некоторые функциональные возможности или данные к объекту, но создание подклассов и / или состав не являются вариантом в этом случае, очевидная вещь, которую нужно сделать, это создать хэш-карту, связывающую объект, который вы хотите расширить, со свойством, которое вы хотите добавить , тогда, когда вам нужно это свойство, вы можете просто посмотреть его на карте. Тем не менее, если объекты, которые вы добавляете, свойства, как правило, разрушаются и создаются много, вы можете получить много старых объектов на вашей карте, занимающих много памяти.

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

Я должен был сделать это , чтобы добавить некоторые данные , java.awt.Componentчтобы обойти изменения в JRE между 1.4.2 и 1.5, я мог бы закрепил его подклассы каждый компонент я был заинтересован Int ( JButton, JFrame, JPanel....) , но это было много проще с гораздо меньшим количеством кода.


1
как WeakHashMap узнает, что «они больше не используются остальной частью вашей программы»?
Винот Кумар CM

2
Слабый hasmap использует слабые ссылки для своих ключей. Когда на объект ссылаются только через слабые ссылки, сборщик мусора «уведомит» владельца слабой ссылки (в данном случае WeaHashMap). Я хотел бы прочитать о WeakReferences и ReferenceQueues в javadocs, чтобы понять, как эти объекты взаимодействуют с сборщиком мусора.
Люк

1
спасибо, Люк, можешь ли ты предоставить простой код для тебя выше описания?
кипяток

Таким образом, слабая ссылка имеет смысл только в Java из-за странного API Java, где некоторые классы не могут быть расширены.
Pacerier

22

Еще один полезный случай для WeakHashMapи WeakReferenceявляется внедрение слушателем реестра .

Когда вы создаете что-то, что хочет прослушивать определенные события, обычно вы регистрируете слушателя, например

manager.registerListener(myListenerImpl);

Если managerваш слушатель хранит с WeakReference, это означает, что вам не нужно удалять регистр, например с, manager.removeListener(myListenerImpl)потому что он будет автоматически удален, как только ваш слушатель или ваш компонент, удерживающий слушателя, станет недоступным.

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

Откуда берется WeakHashMapкартина?

Реестр слушателей, который хочет хранить зарегистрированных слушателей как WeakReferences, нуждается в коллекции для хранения этих ссылок. В WeakHashSetстандартной библиотеке Java нет реализации, WeakHashMapно мы можем легко использовать последнюю для «реализации» функциональности первой:

Set<ListenerType> listenerSet =
    Collections.newSetFromMap(new WeakHashMap<ListenerType, Boolean>());

При этом listenerSetдля регистрации нового прослушивателя вам просто нужно добавить его в набор, и даже если он не будет удален явно, если на прослушиватель больше не ссылаются, он будет автоматически удален JVM.


10
Проблема с использованием weakHashSets для списка слушателей состоит в том, что анонимные экземпляры слушателей, созданные в register (), будут легко потеряны, что будет неожиданно для пользователя. Вместо этого безопаснее хранить более сильные ссылки на слушателей от менеджеров и полагаться на то, что вызывающий абонент сделает правильные вещи.
Гунанареш

Для реализации реестра слушателей: Все зарегистрированные слушатели будут собраны / уничтожены при следующем ударе GC? Например, при запуске метода onSomethingHappened () всех слушателей, что произойдет, если GC пнул?
blackkara

@icza, я не могу поверить, что люди все еще торгуют этим мифом. Это совершенно неправильный ответ. С тем же успехом можно сказать, что еще один полезный случай WeakHashMap- когда вам нужен HashMapобъект. Так ничего себе вам не придется вручную делать hashmap.remove когда - либо , потому что элементы автоматически удаляются после того , как OBJ выходит за рамки! Буквально волшебство! Такой уродливый волшебный хакер - это полноценный маску лица .
Pacerier

3
@Pacerier: я проследовал по вашим ссылкам из других комментариев в JavaScript, и до сих пор не совсем понимаю, почему реализация реестра слушателей с помощью WeakMap - это миф. Например, если клиенты WebSocket должны быть связаны с некоторыми слушателями на них через службу реестра, кажется логичным хранить объекты сокетов как ключи в WeakMap (чтобы они не зависали в памяти после закрытия соединения, скажем, при ошибке) и могли чтобы восстановить всех своих слушателей, если это необходимо. Итак, не могли бы вы заявить, что именно не так с этим подходом?
Поврежденный органический

1
@Pacerier Я тоже не понимаю твои возражения. В сценарии «Публикация-подписка» или «Шина событий» коллекция слабых ссылок имеет для меня смысл. Подписывающемуся объекту разрешено выходить из области видимости и отправляться на сборку мусора без необходимости формальной отписки. Этот процесс отказа от подписки может быть особенно сложным, если сторонний объект отвечал за первоначальную подписку без ведома объекта подписки. Коллекция WeakReferenceзначительно упрощает кодовую базу и позволяет избежать ненужных ошибок, связанных с отказом от подписки. Какой минус?
Базилик Бурк

5

Эта запись блога демонстрирует использование обоих классов: Java: синхронизация по идентификатору . Использование идет примерно так:

private static IdMutexProvider MUTEX_PROVIDER = new IdMutexProvider();

public void performTask(String resourceId) {
    IdMutexProvider.Mutex mutext = MUTEX_PROVIDER.getMutex(resourceId);
    synchronized (mutext) {
        // look up the resource and do something with it
    }
}

IdMutextProvider предоставляет объекты на основе идентификатора для синхронизации. Требования следующие:

  • должен возвращать ссылку на один и тот же объект для одновременного использования эквивалентных идентификаторов
  • должен вернуть другой объект для разных идентификаторов
  • нет механизма освобождения (объекты не возвращаются поставщику)
  • не должно протекать (неиспользуемые объекты имеют право на сборку мусора)

Это достигается с помощью внутренней карты памяти типа:

WeakHashMap<Mutex, WeakReference<Mutex>>

Объект является как ключом, так и значением. Когда ничто внешнее к карте не имеет жесткой ссылки на объект, это может быть сбор мусора. Значения на карте хранятся с жесткими ссылками, поэтому значение должно быть заключено в WeakReference, чтобы предотвратить утечку памяти. Этот последний момент рассматривается в Javadoc .


3

Если вы, например, хотите отслеживать все объекты, созданные определенного класса. Чтобы по-прежнему разрешать сборку этих объектов, вы храните список / карту слабых ссылок на объекты вместо самих объектов.

Теперь, если бы кто-то мог объяснить мне фантомные ссылки, я был бы счастлив ...


2
Одно использование: PhantomReferences позволяет вам точно определить, когда объект был удален из памяти. На самом деле это единственный способ определить это. ( weblogs.java.net/blog/enicholas/archive/2006/05/… )
Джейкоб Кралл

На самом деле он не удаляется, пока вы явно не очистите его. «В отличие от мягких и слабых ссылок, фантомные ссылки не очищаются автоматически сборщиком мусора при постановке в очередь. Объект, доступный через фантомные ссылки, будет оставаться таким до тех пор, пока все такие ссылки не будут очищены или сами не станут недоступными».
Jontro

@jontro, но это уже доработано , все участники ушли. По сути, это пустой объект. См stackoverflow.com/q/7048767/632951
Pacerier

3

Как указано выше, слабая ссылка сохраняется до тех пор, пока существует сильная ссылка.

Примером использования может быть использование WeakReference внутри слушателей, чтобы слушатели перестали быть активными после того, как основная ссылка на их целевой объект исчезла. Обратите внимание, что это не означает, что WeakReference удален из списка слушателей, очистка все еще требуется, но может быть выполнена, например, в запланированное время. Это также препятствует тому, чтобы прослушиваемый объект удерживал сильные ссылки и в конечном итоге становился источником раздувания памяти. Пример: компоненты Swing GUI, ссылающиеся на модель с более длинным жизненным циклом, чем у окна.

Играя со слушателями, как описано выше, мы быстро поняли, что объекты собираются «немедленно» с точки зрения пользователя.


Спасибо, полезный ответ. Но мне интересно, в таком случае, должны ли слушатели быть зарегистрированы (ссылки) сильно?
blackkara

Этот ответ совершенно неверный. Разработка: stackoverflow.com/questions/154724/…
Pacerier

@Pacerier - за WeakReferencesваш комментарий совершенно не правильно!

2

Одно из реальных применений, которое я использовал для WeakReferences, - это если у вас есть один очень большой объект, который редко используется. Вы не хотите хранить это в памяти, когда это не нужно; но, если другой поток нуждается в том же объекте, вам не нужны два из них в памяти. Вы можете хранить где-нибудь слабую ссылку на объект и жесткие ссылки в методах, которые его используют; когда оба метода завершатся, объект будет собран.


1
Это мягкая ссылка, а не слабая ссылка. См stackoverflow.com/a/155492/632951
Pacerier


-1

вы можете использовать weakhashmap для реализации бескресурного кэширования для создания обширного объекта.

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


Вы говорите о мягкой ссылке, а не о слабой. См stackoverflow.com/a/155492/632951
Pacerier
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.