Подтвердить что ты не робот

Как SoftReferences собираются JVM на практике?

У меня есть два отдельных кэша, работающих в JVM (один контролируется третьей стороной), каждый из которых использует мягкие ссылки. Я бы предпочел, чтобы JVM очистил мой контролируемый кеш до того, который контролируется библиотекой. В javadoc SoftReference указано:

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

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

Как обычные реализации JVM, особенно HotSpot, обрабатывают SoftReferences на практике? "Уклоняются ли они от устранения недавно созданных или недавно использованных мягких ссылок", как это рекомендовано спецификацией?

4b9b3361

Ответ 1

Похоже, он может быть настроен, но это не так. Совпадающий сборщик меток зависает при реализации кучи по умолчанию must_clear_all_soft_refs(), который, по-видимому, только true при выполнении _last_ditch_collection.

bool GenCollectedHeap::must_clear_all_soft_refs() {
  return _gc_cause == GCCause::_last_ditch_collection;
}

В то время как обычная обработка неудачного распределения имеет три последовательных вызова метода кучи do_collect, в CollectorPolicy.cpp

HeapWord* GenCollectorPolicy::satisfy_failed_allocation(size_t size,
                                                    bool   is_tlab) {

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

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

  // If we reach this point, we're really out of memory. Try every trick
  // we can to reclaim memory. Force collection of soft references. Force
  // a complete compaction of the heap. Any additional methods for finding
  // free memory should be here, especially if they are expensive. If this
  // attempt fails, an OOM exception will be thrown.
  {
    IntFlagSetting flag_change(MarkSweepAlwaysCompactCount, 1); // Make sure the heap is fully compacted

    gch->do_collection(true             /* full */,
                       true             /* clear_all_soft_refs */,
                       size             /* size */,
                       is_tlab          /* is_tlab */,
                       number_of_generations() - 1 /* max_level */);
  }

--- Отредактировано в ответ на очевидное, я описывал слабые ссылки, а не мягкие ---

На практике я бы предположил, что SoftReferences являются "не", когда JVM вызывается для сбора мусора в ответ на попытки избежать OutOfMemoryError.

Для SoftReference для совместимости со всеми четырьмя сборщиками мусора Java 1.4 и с новым сборщиком G1 решение должно лежать только с определением достижимости. К тому моменту, когда возникают жатвы и уплотнения, слишком поздно решить, доступен ли объект. Это предполагает (но не требует), что существует "контекст" коллекции, который определяет доступность, основанную на доступности свободной памяти в куче. Такой контекст должен был бы указать не следовать SoftReference, прежде чем пытаться следовать им.

Поскольку OutOfMemoryError сбор мусора избегания специально запланирован в режиме полной коллекции, остановившись в мире, было бы нелегко представить сценарий, когда менеджер кучи устанавливает "не следует SoftReference", перед созданием коллекции.

--- Хорошо, поэтому я решил, что ответ "должен работать таким образом" просто не был достаточно хорошим.

Из исходного кода src/share/vm/gc_implementation/concurrentMarkSweep/vmCMSOperations.cpp (основные моменты мои)

Операция на самом деле "делать" сбор мусора:

  170 void VM_GenCollectFullConcurrent::doit() {

Нам лучше быть потоком VM, иначе "программный" поток - сбор мусора!

  171   assert(Thread::current()->is_VM_thread(), "Should be VM thread");

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

  172   assert(GCLockerInvokesConcurrent || ExplicitGCInvokesConcurrent, "Unexpected");
  173 

Возьмите кучу (в ней есть объект GCCause).

  174   GenCollectedHeap* gch = GenCollectedHeap::heap();

Проверьте, нужна ли нам передняя "молодая" коллекция

  175   if (_gc_count_before == gch->total_collections()) {
  176     // The "full" of do_full_collection call below "forces"
  177     // a collection; the second arg, 0, below ensures that
  178     // only the young gen is collected. XXX In the future,
  179     // we'll probably need to have something in this interface
  180     // to say do this only if we are sure we will not bail
  181     // out to a full collection in this attempt, but that's
  182     // for the future.

Не связаны ли потоки программы с кучей?

  183     assert(SafepointSynchronize::is_at_safepoint(),
  184       "We can only be executing this arm of if at a safepoint");

Извлеките причину сбора мусора (причина этой коллекции) из кучи.

  185     GCCauseSetter gccs(gch, _gc_cause);

Сделайте полную коллекцию молодого пространства

Обратите внимание, что его пропуски в значении кучи must_clear_all_soft_refs В сценарии OutOfMemory должно быть установлено значение true, и в любом случае направляет "do_full_collection", чтобы не следовать мягким ссылкам

  186     gch->do_full_collection(gch->must_clear_all_soft_refs(),
  187                             0 /* collect only youngest gen */);

_gc_cause - это перечисление, которое (догадки здесь) установлено на _allocation_failure в первой попытке избежать OutOfMemoryError и _last_ditch_collection после этого не удалось (попытаться собрать временный мусор)

Быстрый просмотр в памяти "кучи" модуля показывает, что в do_full_collection, который вызывает do_collection, мягкие ссылки очищаются явно (в "правильные" условия) с линией

  480   ClearedAllSoftRefs casr(do_clear_all_soft_refs, collector_policy());

--- Оригинальный пост следует тем, кто хочет узнать о слабых ссылках ---

В методе Mark and Sweep Soft-ссылки не следуют из основного потока (и, таким образом, не отмечены, если другая ветка не может достичь этого через немягкие ссылки.

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

В принципе, при следовании сети ссылок из "основного" потока выполнения, мягкие ссылки не соблюдаются. Это позволяет их объектам собирать мусор, как если бы у них не было ссылок, указывающих на них.

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

Ответ 2

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

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

Если тип ресурса не является только выделенным кучей объектом, тогда вы должны потребовать от своих пользователей вызова метода явной освобождения, то есть Closeable.close(). Чтобы защитить от "забытых" вызовов этого метода выпуска, вы можете рассмотреть возможность применения метода finalize(), но остерегайтесь его побочных эффектов. Для получения дополнительной информации об этом, я рекомендую прочитать "Пункт 7: Избегайте финализаторов" от Джошуа Блоха "Эффективная Java (2-е издание)".

Ответ 3

Найдена одна часть информации в FAQ HotSpot, которая может быть устаревшей: http://www.oracle.com/technetwork/java/hotspotfaq-138619.html#gc_softrefs

Что определяет, когда объекты с мягкой ссылкой сброшены?

Начиная с 1.3.1, объекты с мягким достижением будут оставаться живыми для некоторое количество времени после последнего обращения к ним. значение по умолчанию - одна секунда от продолжительности жизни за один мегабайт в куче. Это значение можно настроить с помощью флага -XX: SoftRefLRUPolicyMSPerMB, который принимает целочисленные значения, представляющие миллисекунды. Например, для изменения значения с одной секунды до 2,5 секунд используйте этот флаг:

-XX: SoftRefLRUPolicyMSPerMB = 2500

Виртуальная машина Java HotSpot использует максимально возможный размер кучи (как установлено с опцией -Xmx), чтобы вычислить оставшееся свободное пространство.

Клиент VM Hotspot Client использует текущий размер кучи для вычисления свободное пространство.

Это означает, что общая тенденция заключается в том, что VM куча, а не мягкие ссылки, и поэтому -Xmx имеет значительное влияние на то, когда мягкие ссылки собирают мусор.

С другой стороны, клиентская VM будет иметь большую тенденцию к потоку мягкие ссылки, а не рост кучи.

Поведение, описанное выше, верно для 1.3.1 через Java SE 6 версии виртуальных машин Java HotSpot. Это поведение не является частью виртуальной машины спецификация, однако, и может быть изменена в будущих выпусках. Аналогично, флаг -XX: SoftRefLRUPolicyMSPerMB не гарантирован присутствовать в любом выпуске.

До версии 1.3.1 виртуальные машины Java HotSpot очистили мягкие ссылки когда он их нашел.

Более подробную информацию можно найти по адресу: http://jeremymanson.blogspot.com/2009/07/how-hotspot-decides-to-clear_07.html (любезно предоставлено MiserableVariable)

Ответ 4

Не то, чтобы это было авторитетно, но с использованием SoftReference в ярости я никогда не видел, чтобы виртуальная машина стирала их вместо увеличения размера виртуальной машины. На самом деле я почему-то предполагал, что это так, и дизайн очень сильно зависит от этого. У меня были те же -ms и -mx, но это не имело значения.

Но я не могу найти какой-либо спецификации, которая на самом деле говорит, что это необходимо. Этот блог, похоже, подробно описывает, как SoftReferences очищаются. Из быстрого чтения это действительно похоже на они могут быть очищены, даже если доступна другая память.

Ответ 5

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

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