Долгоживущие Java WeakReferences

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

  • У меня есть дамп кучи после 4-дневного запуска приложения.
  • Этот дамп кучи содержит ~ 800 объектов WeakReference, которые указывают на объекты (все одного типа, которые Я буду называть Foo для целей этого вопроса), сохраняя 40 МБ памяти.
  • Инструмент анализа памяти Eclipse показывает, что на каждый из объектов Foo, на которые ссылаются эти WeakReferences, не ссылаются никакие другие объекты. Я ожидаю, что это должно сделать эти объекты Foo Слабо достижимыми , и, следовательно, они должны быть собраны при следующем сборке мусора.
  • Каждый из этих объектов Foo имеет отметку времени, которая показывает, что они были выделены в течение 4-дневный пробег. У меня также есть журналы за это время, которые подтверждают, что сборка мусора происходила.
  • Мое приложение создает огромное количество объектов Foo, и лишь очень небольшая часть из них попадает в это состояние в дампе кучи. Это наводит на мысль, что основная причина - это своего рода состояние гонки.
  • Мое приложение использует JNI для вызова собственной библиотеки. Код JNI вызывает NewGlobalRef 4 раза во время инициализации в начале дня, чтобы получить ссылки на классы Java, которые он использует.

Что могло привести к тому, что эти классы Foo не собирались, несмотря на то, что на них ссылается только WeakReferences (согласно Eclipse Memory Analyzer Tool) ?

РЕДАКТИРОВАТЬ1:

@mindas

  • Мое приложение создает огромное количество объектов Foo, и лишь очень небольшая их часть оказывается в этом состоянии в дампе кучи. Это наводит на мысль, что основная причина - это своего рода состояние гонки.
  • Мое приложение использует JNI для вызова собственной библиотеки. Код JNI вызывает NewGlobalRef 4 раза во время инициализации в начале дня, чтобы получить ссылки на классы Java, которые он использует.
  • Что могло привести к тому, что эти классы Foo не собирались, несмотря на то, что на них ссылается только WeakReferences (согласно Eclipse Memory Analyzer Tool) ?

    РЕДАКТИРОВАТЬ1:

    @mindas

  • Мое приложение создает огромное количество объектов Foo, и лишь очень небольшая их часть оказывается в этом состоянии в дампе кучи. Это наводит на мысль, что основная причина - это своего рода состояние гонки.
  • Мое приложение использует JNI для вызова собственной библиотеки. Код JNI вызывает NewGlobalRef 4 раза во время инициализации в начале дня, чтобы получить ссылки на классы Java, которые он использует.
  • Что могло привести к тому, что эти классы Foo не собирались, несмотря на то, что на них ссылается только WeakReferences (согласно Eclipse Memory Analyzer Tool) ?

    РЕДАКТИРОВАТЬ1:

    @mindas WeakReference, который я использую, эквивалентен следующему примеру кода:

    public class FooWeakRef extends WeakReference
    {
      public long longA;
      public long longB;
      public String stringA;
    
      public FooWeakRef(Foo xiObject, ReferenceQueue xiQueue)
      {
        super(xiObject, xiQueue);
      }
    }
    

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

    @kasten Слабые ссылки удаляются перед финализацией объекта. Мой дамп кучи показывает, что этого не произошло.

    @jarnbjo Я ссылаюсь на WeakReference Javadoc:

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

    Это наводит на мысль, что сборщик мусора должен обнаруживать тот факт, что мои объекты Foo «Слабо достижимы» и «В то время» очищать слабые ссылки.

    РЕДАКТИРОВАТЬ 2

    @j flemm - Я знаю, что 40 МБ звучит не так много, но меня беспокоит, что 40 МБ за 4 дня означает 4000 МБ за 100 дней. Все документы, которые я прочитал, предполагают, что слабодоступные объекты не должны оставаться поблизости в течение нескольких дней. Поэтому меня интересуют любые другие объяснения того, как на объект можно ссылаться без ссылки, отображаемой в дампе кучи.

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

    РЕДАКТИРОВАТЬ 3

    @jarnbjo - Я понимаю, что у меня нет гарантии относительно того, когда JDK заметит, что объект слабо достижим. Однако я ожидал, что приложение, находящееся под большой нагрузкой в ​​течение 4 дней, предоставит сборщику мусора достаточно возможностей, чтобы заметить, что мои объекты слабо достижимы. Через 4 дня у меня возникли серьезные подозрения, что оставшиеся объекты со слабыми ссылками каким-то образом просочились.

    РЕДАКТИРОВАТЬ 4

    @j flemm - Это действительно интересно! Чтобы уточнить, вы говорите, что сборщик мусора происходит в вашем приложении и не очищает мягкие / слабые ссылки? Не могли бы вы дать мне более подробную информацию о том, какую конфигурацию JVM + GC вы используете? Мое приложение использует панель памяти в 80% кучи для запуска GC. Я предполагал, что любой сборщик мусора старого поколения очистит слабые ссылки. Вы предлагаете, чтобы сборщик мусора собирал слабые ссылки только тогда, когда использование памяти превышает более высокий порог? Можно ли настроить этот более высокий предел?

    РЕДАКТИРОВАТЬ 5

    @j flemm - Ваш комментарий об очистке WeakRefs перед SoftRefs согласуется с Javadoc, в котором говорится: В это время он атомарно очистит все слабые ссылки на этот объект и все слабые ссылки на любые другие слабодоступные объекты, из которых этот объект доступен через цепочку сильных и мягких ссылок. В то же время он объявит все ранее слабодоступные объекты финализируемыми. В то же время или позже он поставит в очередь те недавно очищенные слабые ссылки, которые зарегистрированы в очередях ссылок ».

    Для ясности, вы говорите, что сборщик мусора запускается, когда ваше приложение имеет более 50% свободной памяти и в этом случае он не очищает WeakRefs? Почему сборщик мусора вообще запускается, если ваше приложение имеет> 50% свободной памяти? Я думаю, что ваше приложение, вероятно, просто генерирует очень небольшое количество мусора, и при запуске сборщика он очищает WeakRefs но не SoftRefs.

    РЕДАКТИРОВАТЬ 6

    @j flemm - Другое возможное объяснение поведения вашего приложения состоит в том, что собирается молодой ген, но все ваши слабые и мягкие ссылки находятся в старом поколении и очищаются только тогда, когда старое поколение собираются. Для моего приложения у меня есть статистика, показывающая, что собирается старое поколение, что должно означать, что WeakRefs очищены.

    РЕДАКТИРОВАТЬ 7

    Я назначаю награду по этому вопросу. Я ищу какие-либо правдоподобные объяснения того, как WeakRefs может не очищаться во время сборки мусора. Если ответ заключается в том, что это невозможно, я в идеале хотел бы указать на соответствующие биты OpenJDK, которые показывают, что WeakRefs очищается, как только объект определяется как слабодоступный, и эта слабая достижимость устраняется каждый раз при запуске GC.

    20
    задан mchr 8 October 2010 в 15:19
    поделиться