В чем разница между SoftReference и WeakReference в Java?


812

Какая разница между java.lang.ref.WeakReferenceа java.lang.ref.SoftReference?


9
SoftReferences - это тип (не совсем, но для обсуждения) WeakReferences, которые обычно собираются, когда JVM считает, что не хватает памяти.
Аджит Ганга

5
@AjeetGanga, Слабые слабые ссылки всегда собираются при каждом запуске GC. См stackoverflow.com/a/46291143/632951
Pacerier

Ответы:


928

От понимания слабых ссылок , Итан Николас:

Слабые ссылки

Слабая ссылка , попросту говоря, это ссылка , которая не является достаточно сильным , чтобы заставить объект оставаться в памяти. Слабые ссылки позволяют вам использовать способность сборщика мусора определять доступность для вас, поэтому вам не придется делать это самостоятельно. Вы создаете слабую ссылку, как это:

WeakReference weakWidget = new WeakReference(widget);

а затем в другом месте кода вы можете использовать, weakWidget.get()чтобы получить фактический Widgetобъект. Конечно, слабая ссылка недостаточно сильна, чтобы предотвратить сборку мусора, поэтому вы можете обнаружить (если нет сильных ссылок на виджет), который weakWidget.get()внезапно начнет возвращаться null.

...

Мягкие ссылки

Мягкая ссылка точно как слабая ссылка, за исключением того, что он менее стремится выбросить объект , к которому оно относится. Объект, который является только слабо достижимым (самые сильные ссылки на него WeakReferences), будет отброшен на следующем цикле сборки мусора, но объект, который легко доступен, обычно остается на некоторое время.

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

А Питер Кесслер добавил в комментарии:

Sun JRE рассматривает SoftReferences иначе, чем WeakReferences. Мы пытаемся удержать объект, на который ссылается SoftReference, если нет давления на доступную память. Одна деталь: политика для JRE "-client" и "-server" различна: JRE -client старается сохранить ваш размер небольшим, предпочитая очищать SoftReferences, а не расширять кучу, тогда как JRE -server старается сохранить ваш высокая производительность, предпочитая расширять кучу (если возможно), а не очищать SoftReferences. Один размер не подходит для всех.


7
Сообщение больше недоступно, вы можете найти его на машине обратного хода: web.archive.org/web/20061130103858/http://weblogs.java.net/blog/…
riccardo.tasso

на этот раз архив больше недоступен
user1506104

210

Слабые ссылки собраны с нетерпением. Если GC обнаружит, что объект является слабо достижимым (достижимым только по слабым ссылкам), он немедленно очистит слабые ссылки на этот объект. Таким образом, они хороши для хранения ссылки на объект, для которого ваша программа также хранит (строго ссылаются) «связанную информацию», например, кэшированную информацию об отражении класса, или оболочку для объекта и т. Д. Все, что делает нет смысла сохранять после того, как объект, с которым он связан, является GC-ed. Когда слабая ссылка очищается, она помещается в очередь ссылок, которую ваш код где-то опрашивает, а также отбрасывает связанные объекты. То есть вы сохраняете дополнительную информацию об объекте, но эта информация не нужна, когда объект, на который он ссылается, исчезает. Фактически, в некоторых ситуациях вы можете даже создать подкласс WeakReference и сохранить связанную дополнительную информацию об объекте в полях подкласса WeakReference. Другое типичное использование WeakReference в сочетании с Maps для хранения канонических экземпляров.

SoftReferences, с другой стороны, хороши для кэширования внешних восстанавливаемых ресурсов, поскольку GC обычно задерживает их очистку. Однако гарантируется, что все SoftReferences будут очищены до того, как будет выдан OutOfMemoryError, поэтому они теоретически не могут вызвать OOME [*].

Типичным примером использования является сохранение проанализированной формы содержимого из файла. Вы реализуете систему, в которой вы загружаете файл, анализируете его и сохраняете SoftReference для корневого объекта анализируемого представления. В следующий раз, когда вам понадобится файл, вы попытаетесь получить его через SoftReference. Если вы можете получить его, вы избавили себя от другой загрузки / разбора, и если GC тем временем очистил его, вы перезагрузите его. Таким образом, вы используете свободную память для оптимизации производительности, но не рискуете OOME.

Теперь для [*]. Хранение SoftReference не может вызвать OOME само по себе. Если, с другой стороны, вы по ошибке используете SoftReference для задачи, которая предназначена для использования WeakReference (а именно, вы храните информацию, связанную с объектом, как-то строго ссылающейся, и отбрасываете ее, когда очищается объект Reference), вы можете запустить OOME как Ваш код, который опрашивает ReferenceQueue и удаляет связанные объекты, может не запуститься своевременно.

Таким образом, решение зависит от использования - если вы кэшируете информацию, которую дорого построить, но тем не менее восстанавливаемую из других данных, используйте мягкие ссылки - если вы сохраняете ссылку на канонический экземпляр некоторых данных, или вы хотите иметь ссылку на объект, не «владея» им (таким образом предотвращая его GC'd), используйте слабую ссылку.


14
Особенно полезно для объяснения того, когда будут использоваться слабые объекты.
Джек БеНимбл

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

Я изо всех сил пытаюсь понять, что такое использование WeakHashMap, если он всегда выдает слабую ссылку на свой объект значения ключа?

@supercat, есть только одно правильное использование, WeakReferenceкоторое заключается в наблюдении за запусками GC. См. Подробности: stackoverflow.com/a/46291143/632951
Pacerier

1
@Pacerier: автор этого поста просто неправ. Он пренебрегает некоторыми другими сценариями использования, такими как подписка на события, его вторая точка зрения бессмысленна, а третья точка предполагает, что программист может делать то, что может быть невозможным. Его первое замечание разумно, но напрямую связано с тем, что я сказал. Например, если в коде часто приходится создавать и сравнивать большие неизменяемые объекты, строительная часть часто будет дешевле, если код создает новые объекты, независимо от того, существуют ли они уже, но сравнение объекта и самого себя (идентичные ссылки) будет ...
суперкат

155

На Java ; Порядок от самого сильного до самого слабого, есть: сильный, мягкий, слабый и призрачный

Сильная ссылка нормальная ссылка , которая защищает упомянутый объект из коллекции с помощью ОГО. т.е. никогда не собирает мусор.

Мягкая ссылка имеет право для сбора сборщика мусора, но , вероятно , не будет собираться , пока его память не требуется. т.е. мусор собирает раньше OutOfMemoryError.

Слабая ссылка является ссылкой , что не защищает объект , на который ссылается из коллекции с помощью ГХ. т.е. мусор собирается, когда нет сильных или мягких рефсов.

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

Источник

Аналогия. Предположим, JVM - это королевство, Object - король королевства, а GC - атакующий королевства, который пытается убить короля (объект).

  • Когда король сильный , GC не может убить его.
  • Когда King is Soft , GC атакует его, но King управляет королевством с защитой, пока ресурс не станет доступным.
  • Когда Король слаб , GC атакует его, но управляет королевством без защиты.
  • Когда король Призрак , GC уже убил его, но король доступен через его душу.

7
Мягкая ссылка ... until memory is availableне имеет смысла. Вы имеете в виду is eligible for collection by garbage collector, but probably won't be collected until its memory is needed for another use?
ToolmakerSteve

1
да, сборщик мусора не будет собирать ссылку, пока не освободится память.
Премрай

2
Мне нравятся простые объясненные вещи, без лишних бла бла бла +1 от меня!
Аделин

3
Отличное резюме с инновационным примером
Равиндра Бабу

+1, дальнейшее чтение: javarevisited.blogspot.in/2014/03/…
roottraveller

77

Слабая ссылка http://docs.oracle.com/javase/1.5.0/docs/api/java/lang/ref/WeakReference.html

Принцип: weak reference связан со сборкой мусора. Обычно объект, имеющий один или несколько объектов, не referenceбудет иметь права на сборку мусора.
Вышеуказанный принцип не применим, когда он есть weak reference. Если объект имеет слабую ссылку на другие объекты, он готов к сборке мусора.

Давайте посмотрим на приведенный ниже пример: у нас есть объект Mapwith, где Key ссылается на объект.

import java.util.HashMap;   
public class Test {

    public static void main(String args[]) {
        HashMap<Employee, EmployeeVal> aMap = new 
                       HashMap<Employee, EmployeeVal>();

        Employee emp = new Employee("Vinoth");
        EmployeeVal val = new EmployeeVal("Programmer");

        aMap.put(emp, val);

        emp = null;

        System.gc();
        System.out.println("Size of Map" + aMap.size());

    }
}

Теперь во время выполнения программы мы сделали emp = null. MapХолдинг ключ не имеет смысла здесь , как это null. В описанной выше ситуации объект не является сборщиком мусора.

WeakHashMap

WeakHashMapэто тот, где записи ( key-to-value mappings) будут удалены, когда уже невозможно получить их из Map.

Позвольте мне показать выше пример с WeakHashMap

import java.util.WeakHashMap;

public class Test {

    public static void main(String args[]) {
        WeakHashMap<Employee, EmployeeVal> aMap = 
                    new WeakHashMap<Employee, EmployeeVal>();

        Employee emp = new Employee("Vinoth");
        EmployeeVal val = new EmployeeVal("Programmer");

        aMap.put(emp, val);

        emp = null;

        System.gc();
        int count = 0;
        while (0 != aMap.size()) {
            ++count;
            System.gc();
        }
        System.out.println("Took " + count
                + " calls to System.gc() to result in weakHashMap size of : "
                + aMap.size());
    }
}

Вывод: Взял, 20 calls to System.gc()чтобы привести aMap sizeк: 0.

WeakHashMapимеет только слабые ссылки на ключи, а не сильные ссылки, как другие Mapклассы. Существуют ситуации, о которых вам нужно позаботиться, когда значение или ключ имеют строгую ссылку, хотя вы и использовали WeakHashMap. Этого можно избежать, обернув объект в WeakReference .

import java.lang.ref.WeakReference;
import java.util.HashMap;

public class Test {

    public static void main(String args[]) {
        HashMap<Employee, EmployeeVal> map = 
                      new HashMap<Employee, EmployeeVal>();
        WeakReference<HashMap<Employee, EmployeeVal>> aMap = 
                       new WeakReference<HashMap<Employee, EmployeeVal>>(
                map);

        map = null;

        while (null != aMap.get()) {
            aMap.get().put(new Employee("Vinoth"),
                    new EmployeeVal("Programmer"));
            System.out.println("Size of aMap " + aMap.get().size());
            System.gc();
        }
        System.out.println("Its garbage collected");
    }
}

Мягкие ссылки.

Soft Referenceнемного сильнее, что слабая ссылка. Мягкая ссылка позволяет собирать мусор, но умоляет сборщик мусора очистить его, только если нет другой опции.

Сборщик мусора не собирает агрессивно доступные объекты, как это происходит со слабо достижимыми объектами - вместо этого он собирает объекты, которые легко достижимы, только если ему действительно «нужна» память. Мягкие ссылки - это способ сказать сборщику мусора: «Пока память не слишком тесная, я бы хотел сохранить этот объект. Но если память становится действительно тесной, продолжайте собирать ее, и я разберусь» с этим. " Сборщик мусора требуется очистить все мягкие ссылки, прежде чем он может бросить OutOfMemoryError.


5
Вы можете получить NullPointerExceptionпо прибытию aMap.get().put(...).
xehpuk

Ваш первый пример HashMap выглядит неправильно. Когда вы делаете "aMap.put (emp, val);" и emp, и val являются сильными ссылками. Внутри создается новая переменная для хранения «emp» и «val», поэтому при выполнении «emp = null;» вы просто обнуляете переменную emp, но не внутреннюю переменную для хэш-карты (которая все еще содержит исходный объект Employee). Следовательно, хэш-карта будет по-прежнему содержать сильную ссылку на «emp» независимо от того, что вы делаете с переменной «emp» снаружи.
Тиаго

@Tiago. Нет. Предположительно, «первым примером» вы ссылаетесь на WeakHashMapпример (поскольку это первый пример, демонстрирующий слабое поведение). Посмотрите на документ «WeakHashMap»: "An entry in a WeakHashMap will automatically be removed when its key is no longer in ordinary use. " весь смысл использования WeakHashMap заключается в том, что вам не нужно объявлять / передавать WeakReference; WeakHashMap делает это для вас, внутренне. docs.oracle.com/javase/7/docs/api/java/util/WeakHashMap.html
ToolmakerSteve

Взял 0 вызовов System.gc (), что привело к размеру слабого хэша: 0 - результат вашей второй программы?
Аделин

1
Для другого примера WeakHashMapв действии, с примером приложения, показывающего, как удаляются записи только после выполнения сборки мусора, см. Мой ответ на вопрос: « Всегда ли WeakHashMap постоянно растет, или он очищает ключи мусора? ,
Базилик Бурк

50

Единственная реальная разница между мягкой ссылкой и слабой ссылкой заключается в том, что

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


@АТоррас, Самир. Я расширил этот ответ здесь: stackoverflow.com/a/46291143/632951
Pacerier

25

SoftReferenceпредназначен для кешей. Когда будет обнаружено, что он WeakReferenceссылается на недоступный в противном случае объект, он сразу же будет очищен. SoftReferenceможно оставить как есть. Как правило, существует некоторый алгоритм, касающийся количества свободной памяти и времени, использованного последним, чтобы определить, следует ли ее очистить. Текущий алгоритм Sun предназначен для очистки ссылки, если она не использовалась в течение стольких секунд, поскольку в куче Java есть свободные мегабайты памяти (настраивается, сервер HotSpot проверяет максимально возможную кучу, как установлено -Xmx). SoftReferences будет очищено до того, как OutOfMemoryErrorбудет брошено, если иное не достижимо.


9
Но в Android это не рекомендуется для
кешей

4
@DoctororDrive, вопрос был о Java, а не о Dalvik! :-P
fabspro

2
@YaroslavMytkalyk, честно говоря, если Android хочет переписать поведение класса, он должен использовать свое собственное пространство имен, а не java.lang. Такое злоупотребление синонимами никому не приносит пользы.
Pacerier

9

Эта статья может быть очень полезна для понимания сильных, мягких, слабых и фантомных ссылок.


Чтобы дать вам резюме,

Если у вас есть только слабые ссылки на объект (без сильных ссылок), то объект будет восстановлен GC в следующем цикле GC.

Если у вас есть только мягкие ссылки на объект (без сильных ссылок), то объект будет возвращен GC только тогда, когда JVM не хватит памяти.


Таким образом, вы можете сказать, что сильные ссылки имеют максимальную силу (никогда не могут быть собраны GC)

Мягкие ссылки более эффективны, чем слабые (поскольку они могут избежать цикла GC до тех пор, пока JVM не исчерпает память)

Слабые ссылки даже менее мощны, чем мягкие ссылки (поскольку они не могут спровоцировать какой-либо цикл GC и будут исправлены, если у объекта нет другой сильной ссылки).


Ресторанная аналогия

  • Официант - GC
  • Вы - Объект в куче
  • Зона ресторана / пространство - куча места
  • Новый клиент - новый объект, который хочет столик в ресторане

Теперь, если вы являетесь сильным клиентом (аналог сильной рекомендации), то даже если в ресторан приходит новый клиент или что-то такое, что всегда радует, вы никогда не покинете свой стол (область памяти в куче). Официант не имеет права говорить вам (или даже просить вас) покинуть ресторан.

Если вы мягкий клиент (аналог мягкого отзыва), то, если в ресторан приходит новый клиент, официант не будет просить вас покинуть стол, если не осталось другой пустой таблицы для размещения нового клиента. (Другими словами, официант попросит вас покинуть стол только в том случае, если заходит новый клиент, и для этого нового клиента не осталось другой таблицы)

Если вы слабый клиент (аналог слабой ссылки), тогда официант по своему желанию может (в любой момент) попросить вас покинуть ресторан: P


7

Единственная реальная разница

Согласно документу , свободные WeakReferences должны быть очищены работающим GC.

Согласно документу , свободные SoftReferences должны быть очищены, прежде чем OOM выбрасывается.

Это единственная реальная разница. Все остальное не является частью договора. (Я предполагаю, что последние документы являются договорными.)

SoftReferences полезны. Кэши, чувствительные к памяти, используют SoftReferences, а не WeakReferences.


Единственное правильное использование WeakReference - наблюдать за запуском GC. Вы делаете это, создавая новую WeakReference, чей объект немедленно выходит из области видимости, а затем пытаетесь получить значение null weak_ref.get(). Когда это происходит null, вы узнаете, что между этой продолжительностью GC запускается.

Что касается неправильного использования WeakReference, список бесконечен:

  • бесполезный хак для реализации softreference с приоритетом 2, такой, что вам не нужно писать его, но он не работает должным образом, потому что кеш будет очищаться при каждом запуске GC, даже если есть запасная память. См. Https://stackoverflow.com/a/3243242/632951 для фаз. (Кроме того, что если вам нужно более 2 уровней приоритета кэша? Для этого вам все равно нужна настоящая библиотека.)

  • паршивый хак, чтобы связать данные с объектом существующего класса, но он создает утечку памяти (OutOfMemoryError), когда ваш GC решает сделать перерыв после создания ваших слабых ссылок. Кроме того, это ужасно: лучший подход - использовать кортежи.

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


Как насчет кеша, в котором тип ключа - equals()это просто идентификация объекта? Мягкие ссылки там кажутся пустой тратой, потому что, как только ключевой объект перестает быть сильно доступным, никто никогда не будет искать это отображение снова.
HighCommander4

Я не согласен. Используйте WeakReference, если вы не хотите каким-либо образом влиять на GC (возможно, вы захотите сохранить ссылку на объект, а затем позже проверить, существует ли он, без каких-либо предпочтений). Используйте SoftReference, если вы хотите повлиять на GC, чтобы попытаться сохранить объект (т. Е. Когда вы предпочитаете, чтобы GC сохранял его).
Давид Рафаэли

Хороший пример использования WeakReference в AsyncTask для Android - для сохранения экземпляра контекста. Таким образом, если контекст умирает (если активность - поворот экрана и т. Д.), AsyncTask не будет иметь на него сильную ссылку, и поэтому он может быть собран мусором. Проверьте youtu.be/…
Дэвид Рафаэли

3

Шесть типов состояний достижимости объектов в Java:

  1. Сильно достижимые объекты - GC не будет собирать ( восстанавливать память, занятую ) этим видом объектов. Они достижимы через корневой узел или другой сильно достижимый объект (то есть через локальные переменные, переменные класса, переменные экземпляра и т. Д.)
  2. Мягкие LY доступные объекты - GC может попытаться собрать этот вид объекта в зависимости от раздора памяти. Они доступны из корня через один или несколько мягких эталонных объектов.
  3. Слабо достижимые объекты - GC должен собирать такие объекты. Они доступны из корня через один или несколько слабых эталонных объектов.
  4. Воскрешаемые объекты - GC уже собирает эти объекты. Но они могут вернуться в одно из состояний - Сильное / Мягкое / Слабое при выполнении какого-то финализатора.
  5. Фантомно достижимый объект - GC уже находится в процессе сбора этих объектов и определил, что не может быть воскрешен любым финализатором (если он объявляет сам метод finalize (), то его финализатор будет запущен) . Они доступны из корня через один или несколько фантомных эталонных объектов.
  6. Недостижимый объект - объект не является ни сильно, ни мягко, ни слабо, ни фантомно достижим, и не может быть воскрешен. Эти объекты готовы к утилизации

Для получения более подробной информации: https://www.artima.com/insidejvm/ed2/gc16.html «свернуть


4
Не хорошее описание фантомных ссылок. Кроме того, вы перечислили 4 типа в особом порядке. «Призрак» - самый слабый тип, а не самый сильный тип. Традиционный порядок перечисления это "сильный, мягкий, слабый, призрачный". И я понятия не имею, откуда у вас представление о том, что фантомные объекты используются для механизмов кэширования. AFAIK, это временное состояние, видимое только GC, а не то, с чем будет работать обычный программист.
ToolmakerSteve

@ToolmakerSteve и все - извиняюсь за пару вещей 1. неправильное объяснение ссылок на фантом в предыдущей версии моего ответа, и 2. задержка в исправлении ошибок. Теперь ответ был улучшен путем исправления ошибок
Видясагар

1

Следует помнить, что объект со слабой ссылкой будет собираться только тогда, когда у него ТОЛЬКО слабые ссылки. Если в нем содержится только одна сильная ссылка, она не собирается независимо от того, сколько у нее слабых ссылок.


Это здравый смысл ... то же самое касается softref и фантома.
Pacerier

1

Чтобы дать аспект использования памяти в действии, я провел эксперимент с ссылками Strong, Soft, Weak & Phantom при большой нагрузке с тяжелыми объектами, сохранив их до конца программы. Затем следил за использованием кучи и поведением GC . Эти показатели могут варьироваться от случая к случаю, но, безусловно, дают понимание высокого уровня. Ниже приведены выводы.

Поведение кучи и GC под большой нагрузкой

  • Сильная / Жесткая ссылка - поскольку программа продолжалась, JVM не смогла собрать сохраненный объект с сильной ссылкой. В конце концов я оказался в «java.lang.OutOfMemoryError: Java heap space»
  • Мягкая ссылка - По мере продолжения программы использование кучи продолжало расти, но GD OLD gen случился, когда он приближался к максимальной куче. GC начал немного позже, после запуска программы.
  • Слабая ссылка - Когда программа началась, объекты начали дорабатываться и собираться практически сразу. Чаще всего предметы собирались в мусорную сборку молодого поколения.
  • Ссылка на фантом - Подобно слабой ссылке, объекты, на которые ссылаются фантомы, также начали завершаться и сбор мусора немедленно. ГХ старого поколения не было, и все объекты собирались в самой сборке мусора молодого поколения.

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


0

WeakReference : объекты, на которые имеются только слабые ссылки, собираются при каждом цикле GC (незначительном или полном).

SoftReference : когда объекты, на которые имеются только мягкие ссылки, собираются, зависит от:

  1. -XX: SoftRefLRUPolicyMSPerMB = N флаг (значение по умолчанию 1000, то есть 1 секунда)

  2. Количество свободной памяти в куче.

    Пример:

    • куча имеет 10 МБ свободного места (после полного GC);
    • -XX: SoftRefLRUPolicyMSPerMB = 1000

    Тогда объект, на который ссылается только SoftReference, будет собран, если в последний раз, когда к нему обращались, было больше 10 секунд.

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.