Как заставить мягкие ссылки очищаться в Java? - PullRequest
20 голосов
/ 19 января 2009

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

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

Есть ли какой-нибудь способ заставить Java более охотно очищать "мягкие" ссылки?


Найдено здесь :

"Гарантируется, что все SoftReferences будет очищен раньше OutOfMemoryError выбрасывается, поэтому они теоретически не может вызвать ООМ. "

Значит ли это, что приведенный выше сценарий ДОЛЖЕН означать, что у меня где-то есть утечка памяти, когда какой-то класс содержит жесткую ссылку на мой кэшированный объект?

Ответы [ 10 ]

14 голосов
/ 19 января 2009

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

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

Object temp = new Object();
temp = null;
System.gc();

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


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

Если вы используете опцию -XX:+HeapDumpOnOutOfMemoryError для JVM, а затем загружаете дамп кучи в нечто вроде jhat , вы сможете увидеть все ссылки на ваши объекты и, таким образом, увидеть, есть ли любые ссылки, кроме ваших мягких. В качестве альтернативы вы можете добиться того же с помощью профилировщика во время выполнения теста.

14 голосов
/ 03 марта 2009

Существует также следующий параметр JVM для настройки обработки мягких ссылок:

-XX:SoftRefLRUPolicyMSPerMB=<value>

Где 'значение' - это количество миллисекунд, в течение которых мягкая ссылка будет сохраняться для каждого свободного мегабайта памяти. По умолчанию используется значение 1 с / МБ, поэтому, если объект доступен только программно, он будет работать в течение 1 с, если только 1 МБ пространства кучи свободны.

5 голосов
/ 28 сентября 2010

Вы можете принудительно удалить все SoftReferences в ваших тестах с помощью этого фрагмента кода .

2 голосов
/ 19 января 2009

В типичной реализации JVM (SUN) вам нужно несколько раз запустить Full GC, чтобы очистить Softreferences. Причина этого в том, что Softreferences требуют, чтобы GC выполнял больше работы, например, из-за механизма, который позволяет получать уведомления, когда объекты возвращаются.

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

2 голосов
/ 19 января 2009

Если вы действительно хотите, вы можете вызвать clear () в вашей SoftReference, чтобы очистить его.

Тем не менее, если JVM выдает OutOfMemoryError и ваша SoftReference еще не очищена, то это означает, что вы должны иметь жесткую ссылку на объект где-то еще. В противном случае будет аннулирован контракт SoftReference. В противном случае вам никогда не гарантируется, что SoftReference очищается: пока есть доступная память, JVM не нужно очищать какие-либо SoftReference. С другой стороны, разрешается очищать их в следующий раз, когда выполняется цикл GC, даже если в этом нет необходимости.

Кроме того, вы можете рассмотреть WeakReferences, так как виртуальная машина имеет тенденцию быть более агрессивной в их очистке. Технически, ВМ никогда не требуется очищать WeakReference, но она должна очистить их в следующий раз, когда выполнит цикл GC, если в противном случае объект будет считаться мертвым. Если вы пытаетесь проверить, что происходит, когда ваш кэш очищен, использование WeakReferences должно помочь вашим записям быстрее уходить.

Также помните, что оба они зависят от того, как JVM выполняет цикл GC. К сожалению, нет способа гарантировать, что один из них когда-либо случится. Даже если вы вызываете System.gc (), сборщик мусора может решить, что он делает только что, и выбрать ничего не делать.

1 голос
/ 19 января 2009

Сборка мусора и другие ссылки, такие как мягкие ссылки, не являются детерминированными, так что надежных вещей сделать невозможно, так что мягкие ссылки определенно очищаются в этот момент, поэтому ваш тест может оценить, как реагирует ваш кэш. Я бы посоветовал вам смоделировать очистку ссылок более определенным способом, используя насмешки и т. Д. - ваши тесты будут воспроизводимыми и более ценными, чем просто надежда GC на очистку ссылок. Использование последнего подхода - действительно плохая вещь, и он просто создаст дополнительные проблемы, а не поможет вам улучшить качество вашего кэша и его компонентов для совместной работы.

0 голосов
/ 16 сентября 2009

Есть ли у кэшированного объекта финализатор? Финализатор создаст новые сильные ссылки на объект, поэтому даже если SoftReference очищен, память не будет возвращена до более позднего цикла GC

0 голосов
/ 07 марта 2009

Если у вас есть кеш, который является картой софт-ссылок, и вы хотите, чтобы они были очищены, вы можете просто очистить () карту, и все они будут очищены (включая их ссылки)

0 голосов
/ 19 января 2009

Если вы используете eclipse, существует инструмент под названием Memory Analyzer , который облегчает отладку дампа кучи.

0 голосов
/ 19 января 2009

Из документации и моего опыта я бы сказал, да: у вас должна быть ссылка где-то еще.

Я бы предложил использовать отладчик, который может показать вам все ссылки на объект (например, Eclipse 3.4 при отладке Java 6) и просто проверить, когда выдается OOM.

...