Память Полностью используется Java ConcurrentHashMap (под Tomcat)

Это стек памяти (служит кешем), который состоит из ничего, кроме статического ConcurrentHashMap (CHM).

Все входящие данные HTTP-запроса хранятся в этой ConcurrentHashMap. И есть процесс планировщика asynch, который берет данные из одного и того же ConcurrentHashMap и удаляет ключевое значение после их хранения в базе данных.

Эта система работает нормально и плавно, но просто обнаруживает по следующим критериям: память была полностью использована (2,5 ГБ), и все процессорное время было принято для выполнения GC:

-контактный HTTP-клик 1000/с

-получает тот же одновременный удар в течение 15 минут

Асинхронный процесс регистрирует оставшийся размер CHM каждый раз, когда он записывает в базу данных. CHM.size() поддерживается примерно в диапазоне от мин: 300 до макс.: 3500

Я думал, что в этом приложении есть утечка памяти. поэтому я использовал Eclipse MAT, чтобы посмотреть кучу кучи. После запуска отчета о подозрительности я получил эти комментарии от MAT:

Один экземпляр "org.apache.catalina.session.StandardManager", загруженный "org.apache.catalina.loader.StandardClassLoader @0x853f0280", занимает 2,135,429,456 (94,76%) байт. Память накапливается в одном экземпляре "java.util.concurrent.ConcurrentHashMap $Segment []", загружаемого символом "".

3,646,166 instances of java.util.concurrent.ConcurrentHashMap$Segment retain >= 2,135,429,456 bytes.

и

Length    # Objects      Shallow Heap      Retained Heap 
0         3,646,166      482,015,968       >= 2,135,429,456 

Длина 0 выше я переводит ее как запись пустой длины внутри CHM (каждый раз я вызываю метод CHM.remove()). Он согласуется с количеством записей внутри базы данных, в результате создания этого дампа в базе данных находилось 3,646,166 записей.

Странный сценарий: если я приостанавливаю стресс-тест, использование в памяти кучи будет постепенно снижаться до 25 МБ. Это занимает около 30-45 минут. я повторно имитирую это приложение, и кривые выглядят аналогично графику VisualVM ниже: alt text

Возникает вопрос:

1) Это похоже на утечку памяти?

2) Каждый удаляет вызов remove(Object key, Object value) для удаления <key:value> из CHM, удаляет ли этот объект объект GC?

3) Это как-то связано с настройками GC? Я добавил следующие параметры GC, но без помощи:

-XX:+UseParallelGC

-XX:+UseParallelOldGC

-XX:GCTimeRatio=19

-XX:+PrintGCTimeStamps

-XX:ParallelGCThreads=6

-verbose:gc

4) Любая идея решить это очень ценится!:)

NEW 5) Возможно ли это, потому что все мои ссылки являются твердой ссылкой? Мое понимание до тех пор, пока завершается сеанс HTTP, все те переменные, которые не являются статичными, теперь доступны для GC.

NEW Примечание. Я попытался заменить CHM на ehcache 2.2.0, но я получаю ту же проблему OutOfMemoryException. я полагаю, что ehcache также использует ConcurrentHashMap.

Спецификация сервера:

-Xeon Quad core, 8 потоков.

-4GB Память

-Windows 2008 R2

-Tomcat 6.0.29

Ответы

Ответ 1

У этой проблемы есть ошибка для плохих 7 дней! И, наконец, я обнаружил настоящую проблему! Ниже приведены задачи по тому, что я пробовал, но не смог решить OutOfMemory Exception:

-изменить использование concurrenthashmap для ehcache. (получается, ehcache также использует ConcurrentHashMap)

-обменить всю жесткую ссылку на Soft Reference

-Поддерживаем AbstractMap вместе с concurrnetHashMap в соответствии с предложением Dr. Хайнц М. Кабуц

Вопрос в миллион долларов действительно "зачем 30-45 минут спустя, память начинает возвращаться в кучу?"

Фактическая основная причина заключалась в том, что есть еще что-то еще, содержащее фактический сеанс переменной, и виновником является http-сессия в tomcat, которая по-прежнему активна! Следовательно, даже несмотря на то, что сеанс http был завершен, но если значение тайм-аута составляет 30 минут, tomcat будет хранить информацию о сеансе за 30 минут до того, как JVM сможет их GC. Проблема решена сразу после изменения времени ожидания до 1 минуты в качестве тестирования.

$tomcat_folder\conf\web.xml

<session-config>
    <session-timeout>1</session-timeout>
</session-config>

Надеюсь, что это поможет кому-то там с аналогичной проблемой.

Ответ 2

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

  • Изменить bin/setenv.sh или везде, где JVM args установлены на вашем пусковом устройстве Tomcat:

    Добавить -Dorg.apache.catalina.session.StandardSession.ACTIVITY_CHECK=true

    например.

    # Default Java options
    if [ -z "$JAVA_OPTS" ]; then
            JAVA_OPTS="-server -Djava.awt.headless=true -XX:MaxPermSize=384m -Xmx1024m -Dorg.apache.catalina.session.StandardSession.ACTIVITY_CHECK=true"
    fi
    
  • Изменить conf/context.xml, перед </Context> добавить это:

    <Manager className="org.apache.catalina.session.PersistentManager"
            maxIdleBackup="60" maxIdleSwap="300">
        <Store className="org.apache.catalina.session.FileStore"/>
    </Manager>
    

Перезагрузите Tomcat, и ваша проблема исчезнет, ​​так как он будет хранить ваши сессии, используя файловую систему.

В моем представлении настройка session-timeout = 1 - это обходной путь, который маскирует корень проблемы и неприменим в большинстве приложений, где вам действительно нужно достаточно большое session-timeout. Наши (Bippo) приложения обычно имеют session-timeout из 2880 минут, то есть 2 дня.

Ссылка: Конфигурация диспетчера сеансов Tomcat 7.0

Ответ 3

1) Это похоже на утечку памяти?

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

2) Каждый удалять вызов remove (Object key, Object value) для удаления из CHM, удаляет ли этот объект объект GC?

Объекты могут быть собраны только при сборке мусора, если нет живого (работающего) потока, который имеет ссылку на них. Карта - это только одно место, где есть ссылка на объект. Там могут быть и другие места, которые имеют ссылки на один и тот же объект. Но сохранение объекта на карте предотвратит сбор мусора.

3) Это как-то связано с настройками GC?

Нет; если объект ссылается, он не может быть собран в мусор; неважно, как вы настраиваете сборщик мусора.

Ответ 4

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

Эти 2 ссылки очень полезны
https://issues.apache.org/bugzilla/show_bug.cgi?id=50685
http://wiki.apache.org/tomcat/OutOfMemory

Вкратце, в большинстве случаев это неправильное тестовое или тестовое программное обеспечение. Когда какое-либо пользовательское программное обеспечение открывает URL-адрес, если это программное обеспечение не может управлять http session, tomcat создает новый сеанс для каждого запроса. Например, это можно проверить с помощью простого кода, который можно добавить в JSP.

System.out.println("session id: " + session.getId());
System.out.println("session obj: " + session);
System.out.println("session getCreationTime: " + (new Date(session.getCreationTime())).toString());
System.out.println("session.getValueNames().length: " + session.getValueNames().length);

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

Для некоторого приложения session.getValueNames() длина также важна, потому что, например, когда обычный пользователь работает, он остается тем же, но когда программное обеспечение для тестирования нагрузки делает то же самое, оно растет. Это также означает, что программное обеспечение для тестирования нагрузки не представляет реальной рабочей нагрузки очень хорошо. В моем случае session.getValueNames() длина для обычного пользователя составляла около 100, но с помощью программного обеспечения для тестирования нагрузки через 10 минут было около 500 и, наконец, системные сбои с той же ошибкой OutOfMemory, и MAT показывает то же самое:

org.apache.catalina.loader.StandardClassLoader @0x853f0280 "занимает 2,135,429,456 (94,76%) байт.

Ответ 5

Если вы получаете это исключение и используете spring версию для загрузки 1.4.4 RELEASE или lower, установите значение свойства "server.session-timeout" в минутах, а не то, что они предлагают (в секундах), чтобы сеансы на кучу будут очищены вовремя. Или вы можете использовать bean EmbeddedServletContainerCustomizer, но предоставленное значение будет установлено в минутах.

Пример

(время ожидания сеанса через 10 минут): server.session-timeout = 10 (задано в файле свойств) container.setSessionTimeout(10, TimeUnit.SECONDS); (устанавливается в EmbeddedServletContainerCustomizer)