Вільне суспільство управління пам'яттю в java слабкі і м'які посилання

Прибирання сміття в сучасних мовах покликана позбавити програміста від турбот, пов'язаних з управлінням пам'яттю. У цій статті ми розглянемо два випадки керування автоматичним звільненням пам'яті.

Звичайно, в реальності все складніше: об'єкти поділяються на покоління, використовується механізм «крапчасті карт» та інші. Але для наших цілей достатньо цієї принципової схеми:

Вільне суспільство управління пам'яттю в java слабкі і м'які посилання

Ті посилання, які показані на цій картинці - звичайні. Вони утворюються в момент присвоювання об'єкта деякої змінної:

Для деяких завдань такий підхід може бути здатися занадто негнучким.

Може бути, іноді має сенс зберегти об'єкт, навіть якщо на нього залишилося посилань? Або навпаки - прибрати, навіть якщо він досяжний з кореневого безлічі? Або, може бути, деякі об'єкти має сенс знищувати не відразу, а виконувати деякі дії перед знищенням?

Завдання 1: зникаючі ящики

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

При надходженні на склад, ящик додається в таблицю:

При цьому виникає проблема: при відвантаженні ящика зі складу, пам'ять, виділена для нього, не звільняється, оскільки хеш-таблиця містить посилання на ящик, і віртуальна машина вважає його «живим».

Питання: як можна модифікувати хеш-таблицю так, щоб пам'ять звільнялася після відвантаження ящика?

Одне з можливих рішень:

Це завдання - один з варіантів досить загальної проблеми: є об'єкт зі «середнім» часом життя (тобто він живе довше, ніж метод, який виділив для нього пам'ять, але менше, ніж додаток), c цим об'єктом пов'язані метадані, і цей зв'язок зберігається в словнику. У підсумку, деякого третього об'єкту необхідно в певний момент почистити словник від непотрібних записів. Якщо цей третій об'єкт випадково забуде почистити словник, трапиться витік пам'яті.

Було б добре, якби записи в цьому словнику взагалі не враховувалися при підрахунку досяжності об'єкта - тобто, якби збирач сміття знищував об'єкт box. коли посилання на нього залишилися тільки в словнику.

Слабкі посилання створюються приблизно так:

У тому випадку, якщо об'єкт boxRef залишиться єдиною живою посиланням на об'єкт box. об'єкт box буде видалений складальником сміття:

Вільне суспільство управління пам'яттю в java слабкі і м'які посилання

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

Реалізація такої таблиці - це дуже цікаво, але трохи виходить за рамки цієї замітки. Ми скористаємося стандартним класом WeakHashMap. Одну з можливих реалізацій можна подивитися в Apache Harmony, що увійшла в якості бібліотеки класів в Android.

Завдання 2: вірний шлях до OutOfMemoryError

Вам необхідна перетворити кілька (сотень? Тисяч?) Картинок. Для цього у вас є ось такий код:

Ідея попередньо виділити буфер для файлу здається нормальною: виділення пам'яті щоразу для кожної нової картинки може трохи сповільнити роботу програми. Однак виникає нова проблема: раптом з додатком перестане вистачати пам'яті? Тоді, напевно, розумно буде знехтувати буфером і повернути пам'ять системі.

Питання: яким чином можна модифікувати код так, щоб додаток зберігало буфер, якщо пам'яті достатньо і звільняло його, якщо пам'ять в системі закінчується?

Одне з можливих рішень

Тут нам потрібно поведінку, зворотне поведінки слабких посилань. Ми хочемо вказати збирачеві сміття, що певний тип об'єктів потрібно тримати в пам'яті стільки часу, скільки можливо без ризику отримати OutOfMemoryError.

Цього можна домогтися, використовуючи «м'які посилання». Вони працюють таким чином: якщо об'єкт можна досягти через набір м'яких посилань ( «мягкодостіжім», softly reachable, див. Малюнок), то віртуальна машина буде намагатися зберігати цей об'єкт в пам'яті як можна довше.

Вільне суспільство управління пам'яттю в java слабкі і м'які посилання

Все, що потрібно в нашій задачі - це обернути буфер в м'яку посилання.

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