В настоящее время я пытаюсь диагностировать медленную утечку памяти в моем приложении. На данный момент у меня есть следующие факты.
- У меня есть дамп кучи из 4-дневного прогона приложения.
- Этот дамп кучи содержит ~ 800 объектов WeakReference, которые указывают на объекты (все того же типа, которые я буду называть Foo для целей этого вопроса), сохраняющие 40 МБ памяти.
- Eclipse Memory Analysis Tool показывает, что каждый из объектов Foo, на которые ссылаются эти WeakReferences, не упоминается никакими другими объектами. Я ожидаю, что это должно сделать эти объекты Foo слабо доступными и, таким образом, они должны быть собраны на следующем GC.
- Каждый из этих объектов Foo имеет временную метку, которая показывает, что они были распределены в течение 4-дневного прогона. У меня также есть журналы, которые подтверждают, что сборка мусора происходила.
- Мое приложение создает огромное количество объектов Foo, и только очень малая их часть попадает в это состояние в дампе кучи. Это говорит мне о том, что коренной причиной является какое-то состояние расы.
- Мое приложение использует JNI для вызова нативной библиотеки. Код JNI вызывает NewGlobalRef 4 раза в начале инициализации дня, чтобы получить ссылки на используемые им классы Java.
Что может привести к тому, что эти классы Foo не будут собираться, несмотря на то, что на них ссылается только WeakReferences (согласно Eclipse Memory Analyzer Tool)?
EDIT1:
@ Миндаш
Слабая ссылка, которую я использую, эквивалентна следующему примеру кода.
public class FooWeakRef extends WeakReference<Foo>
{
public long longA;
public long longB;
public String stringA;
public FooWeakRef(Foo xiObject, ReferenceQueue<Foo> xiQueue)
{
super(xiObject, xiQueue);
}
}
Foo не имеет финализатора, и любой финализатор не будет рассматриваться, пока WeakRefs не были очищены. Объект не может быть завершен, когда он слабо достижим. Подробности смотрите на этой странице .
@ kasten Слабые ссылки очищаются до завершения объекта. Мой дамп кучи показывает, что этого не произошло.
@ jarnbjo Я ссылаюсь на Javadoc WeakReference:
"Предположим, что сборщик мусора определяет в определенный момент времени, что объект является слабо достижимым. В это время он будет атомарно очищать все слабые ссылки на этот объект и все слабые ссылки на любые другие слабодоступные объекты, из которых это объект доступен через цепочку сильных и мягких ссылок. "
Это наводит меня на мысль, что сборщик мусора должен обнаружить тот факт, что мои объекты Foo «слабо достижимы» и «в это время» очищают слабые ссылки.
РЕДАКТИРОВАТЬ 2
@ j flemm - я знаю, что 40 МБ звучит не так много, но я беспокоюсь, что 40 МБ за 4 дня означает 4000 МБ за 100 дней. Все документы, которые я прочитал, предполагают, что объекты, которые плохо доступны, не должны торчать в течение нескольких дней. Поэтому меня интересуют любые другие объяснения того, как можно строго ссылаться на объект без ссылки, отображаемой в дампе кучи.
Я собираюсь попытаться выделить несколько больших объектов, когда присутствуют некоторые из этих висящих объектов Foo, и посмотреть, собирает ли их JVM. Однако этот тест займет несколько дней для настройки и завершения.
РЕДАКТИРОВАТЬ 3
@ jarnbjo - Я понимаю, что у меня нет гарантии, когда JDK заметит, что объект слабо доступен. Однако я ожидаю, что приложение под большой нагрузкой в течение 4 дней предоставит GC достаточно возможностей, чтобы заметить, что мои объекты слабо доступны. Через 4 дня я сильно подозреваю, что оставшиеся слабо ссылочные объекты как-то просочились.
РЕДАКТИРОВАТЬ 4
@ j flemm - Это действительно интересно!Просто чтобы уточнить, вы говорите, что GC происходит в вашем приложении и не очищает Soft / Weak refs?Не могли бы вы дать мне более подробную информацию о том, какую конфигурацию JVM + GC вы используете?Мое приложение использует панель памяти в 80% кучи для запуска GC.Я предполагал, что любой GC старого поколения очистит слабых рефери.Вы предлагаете, чтобы ГХ собирал Слабые ссылки только после того, как использование памяти превысило более высокий порог?Настраивается ли этот верхний предел?
РЕДАКТИРОВАТЬ 5
@ j flemm - Ваш комментарий об очистке WeakRefs до SoftRefs соответствует Javadoc, который гласит: SoftRef: «Предположим, что сборщик мусора определяет вопределенный момент времени, когда объект является мягко достижимым. В это время может выбрать атомарную очистку всех мягких ссылок на этот объект и всех мягких ссылок на любые другие мягко достижимые объекты, из которых этот объект достижимчерез цепочку сильных ссылок. В то же время или в более позднее время он ставит в очередь те недавно очищенные мягкие ссылки, которые зарегистрированы в очередях ссылок. "
WeakRef:" Предположим, что сборщик мусора определяет вопределенный момент времени, когда объект слабо достижим. В это время он будет атомно очищать все слабые ссылки на этот объект и все слабые ссылки на любые другие слабо достижимые объекты, из которых этот объект доступен через цепочкуиз стройнг и мягкие ссылки.В то же время он объявит, что все ранее слабо достижимые объекты были завершены.В то же время или в более позднее время он будет ставить в очередь те недавно очищенные слабые ссылки, которые зарегистрированы в очередях ссылок. "
Для ясности вы говорите, что сборщик мусора запускается, когда в вашем приложении более 50% свободной памяти, и в этом случае он не очищает WeakRefs. Зачем вообще запускать сборщик мусора, когда в вашем приложении> 50% свободной памяти? Я думаю, что ваше приложение, вероятно, просто генерирует очень небольшое количество мусора и когда его выполняет сборщикочищает WeakRefs, но не SoftRefs.
EDIT 6
@ j flemm - Другое возможное объяснение поведения вашего приложения состоит в том, что молодой ген собирается, но все ваши слабые и мягкие реферив старом гене и очищаются только при сборе старого. Для моего приложения у меня есть статистика, показывающая, что старый ген собирается, что должно означать, что WeakRefs очищены.
РЕДАКТИРОВАТЬ 7
Я начинаю вознаграждение по этому вопросу. Я ищу любые правдоподобные объяснения того, как WeakRefs может не удаляться, пока происходит GC.Если ответ заключается в том, что это невозможно, в идеале я хотел бы указать на соответствующие биты OpenJDK, которые показывают, что WeakRefs очищаются, как только определено, что объект является слабо достижимым, и что слабая достижимость разрешается при каждом запуске GC.