У меня есть кеш, в котором есть мягкие ссылки на кешированные объекты. Я пытаюсь написать функциональный тест для поведения классов, которые используют кеш специально для того, что происходит, когда кешированные объекты очищаются.
Проблема в том, что я не могу надежно очистить мягкие ссылки. Простое использование кучи памяти не помогает: я получаю OutOfMemory до того, как будут очищены какие-либо мягкие ссылки.
Есть ли способ заставить Java более охотно убирать мягкие ссылки?
Найдено здесь:
"It is guaranteed though that all SoftReferences will get cleared before OutOfMemoryError is thrown, so they theoretically can't cause an OOME."
Значит ли это, что приведенный выше сценарий ДОЛЖЕН означать, что у меня где-то есть утечка памяти с каким-то классом, содержащим жесткую ссылку на мой кэшированный объект?




Сборка мусора и другие ссылки, такие как мягкие ссылки, недетерминированы, поэтому на самом деле невозможно надежно делать что-то, так что мягкие ссылки определенно очищаются в этот момент, поэтому ваш тест может оценить, как реагирует ваш кеш. Я бы посоветовал вам смоделировать очистку ссылок более определенным образом, высмеивая и т. д. - ваши тесты будут воспроизводимыми и более ценными, чем просто Hopi g для GC для очистки ссылок. Использование последнего подхода - действительно плохой поступок, и он просто создаст дополнительные проблемы, а не поможет вам улучшить качество вашего кеша и взаимодействующих с ним компонентов.
Судя по документации и моему опыту, я бы сказал, что да: у вас должна быть ссылка где-то еще.
Я бы предложил использовать отладчик, который может показывать вам все ссылки на объект (например, Eclipse 3.4 при отладке Java 6) и просто проверять, когда запускается OOM.
The problem is: I can't seem to reliably get the soft references to be cleared.
Это не уникально для SoftReferences. Из-за природы сборки мусора в Java нет гарантии, что все, что можно собирать, будет действительно собрано в любой момент времени. Даже с простым фрагментом кода:
Object temp = new Object();
temp = null;
System.gc();нет никакой гарантии, что объект, созданный в первой строке, будет собран сборщиком мусора на этом или фактически в любой другой точке. Это просто одна из вещей, с которой вам приходится жить в языке с управляемой памятью, вы отказываетесь от декларативной власти над этими вещами. И да, иногда это может затруднить окончательную проверку на утечку памяти.
Тем не менее, согласно процитированным вами Javadocs, SoftReferences обязательно должны быть очищены до того, как будет выбрано OutOfMemoryError (фактически, в этом весь их смысл и единственный способ их отличия от ссылок на объекты по умолчанию). Таким образом, это будет звучать так, как будто есть какая-то утечка памяти в том, что вы держитесь за более жесткие ссылки на рассматриваемые объекты.
Если вы используете опцию -XX:+HeapDumpOnOutOfMemoryError для JVM, а затем загружаете дамп кучи во что-то вроде джхат, вы должны иметь возможность видеть все ссылки на свои объекты и, таким образом, видеть, есть ли какие-либо ссылки помимо ваших программных. В качестве альтернативы вы можете добиться того же с помощью профилировщика во время выполнения теста.
+1 для ссылки на jhat, похоже, очень полезный инструмент. Есть ли что-нибудь подобное для .NET?
Чтобы ответить на свой вопрос: microsoft.com/downloads/…
Если вы используете eclipse, есть инструмент под названием Анализатор памяти, который упрощает отладку дампа кучи.
Если вы действительно этого хотите, вы можете вызвать clear () в своем SoftReference, чтобы очистить его.
Тем не менее, если JVM выдает OutOfMemoryError, а ваша SoftReference еще не очищена, это означает, что у вас должна быть жесткая ссылка на объект в другом месте. В противном случае договор SoftReference станет недействительным. В противном случае вам никогда не будет гарантировано, что SoftReference будет очищен: пока есть доступная память, JVM не нужно очищать какие-либо SoftReferences. С другой стороны, разрешено очистить их в следующий раз, когда он выполнит цикл GC, даже если в этом нет необходимости.
Кроме того, вы можете рассмотреть возможность изучения WeakReferences, поскольку виртуальная машина, как правило, более агрессивно очищает их. Технически от виртуальной машины никогда не требуется очищать WeakReference, но предполагается, что она очищает их в следующий раз, когда она выполняет цикл сборки мусора, если объект в противном случае будет считаться мертвым. Если вы пытаетесь проверить, что происходит при очистке кеша, использование WeakReferences должно помочь вашим записям уйти быстрее.
Также помните, что оба они зависят от JVM, выполняющего цикл сборки мусора. К сожалению, нет никакой гарантии, что такое когда-либо произойдет. Даже если вы вызовете System.gc (), сборщик мусора может решить, что он работает просто замечательно, и ничего не делать.
В типичной реализации JVM (SUN) вам нужно запускать полный сборщик мусора более одного раза, чтобы очистить Softreferences. Причина этого в том, что Softreferences требуют от GC выполнять больше работы, например, из-за механизма, который позволяет вам получать уведомления, когда объекты возвращаются.
ИМХО, использование большого количества softreferences на сервере приложений - зло, потому что разработчик не имеет большого контроля над тем, когда они будут выпущены.
Согласитесь с первым пунктом (правильно, в первый раз учитываются только часы). Не согласен со вторым пунктом - слишком общим.
Как вы думаете, почему это слишком общее. Хорошо, если у вас есть только одно приложение на вашем сервере, чем это возможно. Но я думаю, что это очень редко. И даже в этом случае политика часто бывает не такой, как вам хотелось бы.
Существует также следующий параметр JVM для настройки обработки мягких ссылок:
-XX:SoftRefLRUPolicyMSPerMB=<value>
Где «значение» - это количество миллисекунд, в котором мягкая ссылка будет оставаться для каждого свободного мегабайта памяти. Значение по умолчанию - 1 с / Мб, поэтому, если объект является только программной достижимостью, он будет длиться 1 с, если свободен только 1 Мб кучи.
Если у вас есть кеш, который представляет собой карту SoftReferences, и вы хотите, чтобы они были очищены, вы можете просто очистить () карту, и все они будут очищены (включая их ссылки)
-1. Нет, это не то же самое, что очистка ссылок. Это только приводит к тому, что SoftReference становится недоступным (до тех пор, пока нет других сильных ссылок на SoftReference). Если SoftReference в результате gc'ed, то он не будет помещен в очередь. Если вы использовали ReferenceQueue для уведомления вас, когда SoftReference был очищен, это предотвратит вызов метода очистки.
@finw, на самом деле ответ имеет свои достоинства, и это, вероятно, лучшее, что вы можете сделать, если вы хотите, чтобы вас уведомляли, не используйте soft-ref, а фантомные вместе с soft-ref для обеспечения механизма кеширования.
Есть ли у кешированного объекта финализатор? Финализатор создаст новые сильные ссылки на объект, поэтому даже если SoftReference очищен, память не будет восстановлена до следующего цикла сборки мусора.
Вы можете принудительно очистить все SoftReferences в ваших тестах с помощью этого кусок кода.
Но опять же, очистка ссылок во время OOM - это определенное поведение, и это в значительной степени то, чего ему нравится достигать с помощью SoftReferences (если я правильно понимаю), поэтому я бы сказал, что он должен проверить это.