В настоящее время я пытаюсь диагностировать медленную утечку памяти в моем приложении. Факты, которые я имею до сих пор, заключаются в следующем.
- У меня есть куча дампа с 4-дневного запуска приложения.
- Этот кучи кучи содержит ~ 800 объектов WeakReference, которые указывают на объекты (все те же типы, которые я буду называть Foo для целей этого вопроса), сохраняя 40 МБ памяти.
- Инструмент анализа памяти Eclipse показывает, что каждый из объектов Foo, на которые ссылаются эти WeakReferences, не ссылается ни на какие другие объекты. Мое предположение состоит в том, что это должно сделать эти объекты Foo Weakly Reachable и, следовательно, их следует собирать в следующем GC.
- Каждый из этих объектов Foo имеет отметку времени, которая показывает, что они были распределены в течение 4-дневного прогона. У меня также есть журналы за это время, которые подтверждают, что сборка мусора происходила.
- Огромное количество объектов Foo создается моим приложением, и только очень небольшая часть из них заканчивается в этом состоянии в дампе кучи. Это говорит мне о том, что первопричина - это какое-то состояние расы.
- Мое приложение использует JNI для вызова в родную библиотеку. Код JNI вызывает NewGlobalRef 4 раза в начале инициализации дня, чтобы получить ссылки на классы Java, которые он использует.
Что может привести к тому, что эти классы Foo не будут собраны, несмотря на то, что ссылаются только на WeakReferences (в соответствии с Eclipse Memory Analyzer Tool)?
EDIT1:
@mindas Используемый мной метод WeakReference эквивалентен следующему примеру кода.
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 не имеет финализатора, и любой финализатор не будет рассматриваться, пока не будут очищены WeakRef. Объект не может быть финализирован, когда он слабо доступен. Подробнее см. эту страницу.
@kasten. Слабые ссылки очищаются до того, как объект станет окончательным. Мой сброс кучи показывает, что этого не произошло.
@jarnbjo Я ссылаюсь на WeakReference Javadoc:
"Предположим, что сборщик мусора определяет в определенный момент времени, что объект является слабо доступным. В то время он будет атомарно ясно все слабые ссылки на этот объект и все слабые ссылки на любые другие слабо достижимых объектов, из которых, что объект достигнут через цепочку сильных и мягких ссылок".
Это говорит мне о том, что GC должен обнаруживать тот факт, что мои объекты Foo "слабо достижимы" и "в это время" очищают слабые ссылки.
ИЗМЕНИТЬ 2
@j flemm - я знаю, что 40mb звучит не так много, но я волнуюсь, что 40mb за 4 дня означает 4000mb за 100 дней. Все документы, которые я прочитал, показывают, что объекты, которые слабо доступны, не должны находиться в течение нескольких дней. Поэтому меня интересуют любые другие объяснения о том, как объект может быть сильно привязан без ссылки, отображаемой в дампе кучи.
Я собираюсь попытаться выделить некоторые крупные объекты, когда некоторые из этих оборванных объектов Foo присутствуют и посмотреть, собирает ли их JVM. Однако для установки и завершения этого теста потребуется несколько дней.
ИЗМЕНИТЬ 3
@jarnbjo - я понимаю, что у меня нет гарантии, что когда JDK заметит, что объект слабо доступен. Однако я ожидал бы, что приложение под большой нагрузкой в течение 4 дней предоставит достаточные возможности для GC, чтобы заметить, что мои объекты слабо доступны. Через 4 дня я очень подозрительно, что оставшиеся объекты с недостатками ссылок были как-то утечены.
ИЗМЕНИТЬ 4
@j flemm - Это действительно интересно! Чтобы уточнить, вы говорите, что GC происходит в вашем приложении и не очищает Soft/Weak refs? Можете ли вы дать мне более подробную информацию о том, что вы используете JVM + GC Config? Мое приложение использует панель памяти на 80% от кучи для запуска GC. Я предполагал, что любой GC старого поколения очистит Weak refs. Вы предполагаете, что GC собирает только слабые ссылки, когда использование памяти превышает более высокий порог? Является ли этот более высокий лимит настраиваемым?
ИЗМЕНИТЬ 5
@j flemm - Ваш комментарий об очистке WeakRef до SoftRefs соответствует Javadoc, который гласит: SoftRef: "Предположим, что сборщик мусора в определенный момент времени определяет, что объект находится в недоступном состоянии. В это время он можетвыбрать очистить атомарно все мягкие ссылки на этот объект и все мягкие ссылки на любые другие объекты, недоступные для достижения цели, через которые можно достичь через цепочку сильных ссылок. В то же время или в какой-то более поздний момент он выведет в очередь эти вновь очищенные ссылки, зарегистрированные в контрольных очередях.
WeakRef: "Предположим, что сборщик мусора в определенный момент времени определяет, что объект слабо доступен. В это время он будет атомарно очищать все слабые ссылки на этот объект и все слабые ссылки на любые другие недостижимые объекты, из которых этот объект достигнут через цепочку сильных и мягких ссылок. В то же время он объявит все ранее недостижимые объекты окончательными. В то же время или в более позднее время введут в очередь те недавно очищенные слабые ссылки, которые зарегистрированы в контрольных очередях".
Для ясности вы говорите, что сборщик мусора работает, когда ваше приложение имеет более 50% свободной памяти, и в этом случае он не очищает WeakRefs? Почему GC работает вообще, когда ваше приложение имеет > 50% свободной памяти? Я думаю, что ваше приложение, вероятно, просто генерирует очень малое количество мусора, и когда коллекционер работает, он очищает WeakRefs, но не SoftRefs.
ИЗМЕНИТЬ 6
@j flemm - Другое возможное объяснение поведения вашего приложения заключается в том, что молодой ген собирается, но ваши ваши слабые и мягкие ссылки все в старом гене и очищаются только тогда, когда собирается старый ген. Для моего приложения у меня есть статистика, показывающая, что собирается старый ген, что должно означать, что WeakRefs очищаются.
EDIT 7
Я начинаю щедрость по этому вопросу. Я ищу какие-либо правдоподобные объяснения того, как WeakRefs не могут быть очищены во время GC. Если ответ заключается в том, что это невозможно, я бы идеально хотел бы указать на соответствующие биты OpenJDK, которые показывают, что WeakRefs очищаются, как только объект определяется как слабо доступный, и что слабая достижимость разрешается каждый раз, когда выполняется GC.