Коли ви використовуєте WeakHashMap або WeakReference?


163

Використання слабких посилань - це те, що я ніколи не бачив втілення, тому я намагаюся розібратися, що таке приклад використання для них та як буде працювати реалізація. Коли вам потрібно було використовувати WeakHashMapабо WeakReferenceяк і як його застосовували?



Відповіді:


96

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

Оскільки кеш зображень повинен заважати нам перезавантажувати зображення, коли нам абсолютно не потрібно, ви швидко зрозумієте, що кеш завжди повинен містити посилання на будь-яке зображення, яке вже є в пам'яті. Однак із звичайними чіткими посиланнями саме ця посилання змусить зображення залишатись у пам'яті, що вимагає, щоб ви якось визначили, коли зображення більше не потрібно в пам'яті, і видалити його з кеша, щоб воно стало придатним для збору сміття. Ви змушені дублювати поведінку сміттєзбірника та вручну визначати, чи повинен об’єкт бути в пам'яті.

Розуміючи слабкі посилання , Етан Ніколас


43
Чи не були б SoftReferences кращими в цьому випадку, тобто посилання, які збираються лише тоді, коли пам’ять починає вичерпуватися.
JesperE

Я трохи розгублений ... скажімо, у мене є кеш SWT-зображення. Зображення SWT потрібно розкрити методом dispose (), щоб звільнити ресурси SO. Якщо я використовую WeakHashMap для їх зберігання, покажіть, чи точно GC розпоряджатиметься об'єктом?
марколопи

2
@marcolopes GC використовує фіналізатор для будь-якого іншого об'єкта. Здається, SWT це не любить, коли ви це робите, тому я не думаю, що ви можете керувати ресурсами операційної системи за допомогою WeakHashMap.
Джейкоб Кралл

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

Стаття зараз на веб-
mjn42

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 знає, що "вони більше не використовуються в іншій частині вашої програми"?
Vinoth Kumar CM

2
Слабка карта має слабкі посилання для своїх ключів. Якщо на об'єкт посилаються лише через слабкі посилання, збирач сміття буде "сповіщати" власника про слабку посилання (у цьому випадку WeaHashMap). Я читав би про WeakReferences та ReferenceQueues в javadocs, щоб зрозуміти, як ці об’єкти взаємодіють зі смітником.
luke

1
дякую Луке, ви можете надати простий код для вас обов`язкового опису?
кип’ячена вода

Таким чином, слабке відношення має сенс лише у Java через химерний API Java, де деякі класи неможливо розширити.
Pacerier

22

Ще один корисний випадок WeakHashMapі WeakReferenceє реалізація реєстру для слухачів .

Коли ви створюєте щось, що хоче слухати певні події, зазвичай ви реєструєте слухача, наприклад

manager.registerListener(myListenerImpl);

Якщо managerзберігається ваш слухач із символом a WeakReference, це означає, що вам не потрібно видаляти реєстр, наприклад, з-за того, manager.removeListener(myListenerImpl)що він буде автоматично видалений, як тільки ваш слухач або ваш компонент, який тримає слухача, стануть недоступними.

Звичайно, ви все одно можете видалити слухача вручну, але якщо ви цього не зробите або забудете, це не спричинить витік пам’яті та не завадить вашому слухачеві збирати сміття.

Де WeakHashMapвходить у картину?

Реєстр слухачів, який бажає зберігати зареєстрованих слухачів, як WeakReferences потребує колекції для зберігання цих посилань. У WeakHashSetстандартній бібліотеці Java немає жодної реалізації, WeakHashMapале ми можемо легко використовувати останню для "реалізації" функціональності першої:

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

Зважаючи на це, listenerSetщоб зареєструвати нового слухача, ви просто повинні додати його до набору, і навіть якщо він не буде видалено явно, якщо слухач більше не посилається, він автоматично буде видалений JVM.


10
Проблема з використанням слабкого списку слухачів для списку слухачів полягає в тому, що анонімні екземпляри слухачів, створені в регістрі (), легко втрачаються, що може бути несподіваним для користувача. Безпечніше, замість того, щоб більш чіткі посилання на слухачів від менеджерів замість цього, і покладатися на те, щоб абонент зробив правильну справу замість цього.
Гунанареш

Для реалізації реєстру слухачів: Усі зареєстровані слухачі будуть зібрані / знищені під час наступного удару GC? Наприклад, під час запуску всіх слухачів за методомSomethingHappened (), що станеться, якщо GC б'є?
blackkara

@icza, я не можу повірити, що люди все ще розбирають цей міф. Це абсолютно неправильна відповідь. Ви також можете сказати, що ще один корисний випадок - WeakHashMapце коли вам потрібен HashMapпредмет. Так що нічого не потрібно вручну робити hashmap.remove коли-небудь, оскільки елементи автоматично видаляються, коли об'єкт виходить за межі! Буквально магія! Такий некрасивий магічний хак - це повне обличчя .
Pacerier

3
@Pacerier: Я переслідував ваші посилання з інших коментарів у JavaScript на землі аж донизу, і досі не зовсім розумію, чому реалізація реєстру слухачів за допомогою WeakMap є міфом. Наприклад, якщо клієнти WebSocket повинні бути пов'язані з деякими слухачами на них за допомогою сервісу реєстру, здається, що логічно зберігати об'єкти сокет як ключі у WeakMap (щоб запобігти їх висінню в пам'яті після закриття, скажімо, помилки) та можливості за потреби привернути всіх своїх слухачів. Отже, чи не могли б ви сказати, що саме з цим підходом не так?
Пошкоджений органічний

1
@Pacerier Я теж не розумію вашого заперечення. У сценарії Publish-Subscribe або Event Bus колекція слабких посилань має для мене ідеальний сенс. Об'єкту, що підписався, дозволяється виходити за межі сфери та прямувати до вивезення сміття без необхідності офіційно скасовувати підписку. Цей процес скасування передплати може бути особливо складним, якщо сторонній об'єкт відповідав за початкову підписку без відома передплачуваного об'єкта. Колекція 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, щоб запобігти витоку пам'яті. Цей останній пункт висвітлюється в Явадоку .


3

Наприклад, ви хочете відслідковувати всі об’єкти, створені певного класу. Щоб дозволити збирати ці об’єкти сміття, ви зберігаєте список / мапу слабких посилань на об'єкти замість самих об’єктів.

Тепер, якщо хтось міг би пояснити фантомні посилання на мене, я був би радий ...


2
Одне використання: PhantomReferences дозволяють точно визначити, коли об’єкт був вилучений із пам'яті. Насправді вони єдиний спосіб це визначити. ( weblogs.java.net/blog/enicholas/archive/2006/05/… )
Якоб

Насправді він не видаляється, поки ви прямо не очистите його. "На відміну від м'яких і слабких посилань, посилання на фантоми не збираються автоматично збирачем сміття, оскільки вони зачиняються. Об'єкт, який можна отримати за допомогою посилань на фантоми, залишатиметься таким чином, поки всі такі посилання не будуть очищені або самі не стануть недоступними."
jontro

@jontro, Але це вже було завершено , усі члени організації пішли. Ефективно, це порожній obj. Див stackoverflow.com/q/7048767/632951
Pacerier

3

Як було сказано вище, слабкі посилання зберігаються до тих пір, поки існує сильна посилання.

Прикладом використання може бути використання WeakReference всередині слухачів, щоб слухачі перестали бути активними, коли основна посилання на їх цільовий об’єкт не зникне. Зауважте, що це не означає, що WeakReference видалено зі списку слухачів, очищення все ще потрібно, але його можна виконати, наприклад, у запланований час. Це також призводить до того, що об'єкт, який слухають, не дозволяє мати чіткі посилання і, зрештою, стати джерелом розгалуження пам'яті. Приклад: розгортання компонентів графічного інтерфейсу для перегляду моделі, що має довший життєвий цикл, ніж вікно.

Граючи зі слухачами, як описано вище, ми швидко зрозуміли, що об'єкти збираються «негайно» з точки зору користувача.


Дякую корисну відповідь. Але мені цікаво, чи в такому випадку слухачі повинні реєструватися (посилатися на них) сильно?
blackkara

Ця відповідь абсолютно неправильна. Розробка: stackoverflow.com/questions/154724 / ...
Pacerier

@Pacerier - для WeakReferencesвашого коментаря абсолютно неправильно!

2

Єдине реальне використання, яке я мав для WeakReferences, - якщо у вас є один, дуже великий об'єкт, який рідко використовується. Ви не хочете зберігати його в пам'яті, коли це не потрібно; але якщо інший потік потребує того самого об'єкта, ви також не хочете двох з них в пам'яті. Ви можете зберігати слабке посилання на об'єкт десь і жорсткі посилання на методи, які його використовують; коли обидва методи закінчуються, об’єкт буде зібраний.


1
Це softreference, а не слабке. Див stackoverflow.com/a/155492/632951
Pacerier


-1

Ви можете використовувати слабку карту для реалізації кешованого без ресурсів керування для створення експансивного об'єкта.

але зауважте, що не бажано мати об'єкти, що змінюються. Я використовував його для кешування результатів запитів (на виконання яких потрібно близько 400 мс) в текстовій пошуковій системі, яка рідко оновлюється.


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