Управление памятью Tomcat

Я использую Tomcat7, сервер достаточно мощный, 8 ГБ ОЗУ 8-ядерный.

tomcat забирает МНОГО памяти впустую ?

Моя проблема в том, что память RES становится все больше и больше, пока сервер просто не отвечает, даже не вызывая OnOutOfMemoryError.

Конфигурация Томката:

-Xms1024M
-Xmx2048M
-XX:PermSize=256m
-XX:MaxPermSize=512m
-XX:+UseConcMarkSweepGC
-XX:OnOutOfMemoryError='/var/tomcat/conf/restart_tomcat.sh'

Информация о памяти:

Memory:     Non heap memory = 106 Mb (Perm Gen, Code Cache),
Loaded classes = 14,055,
Garbage collection time = 47,608 ms,
Process cpu time = 4,296,860 ms,
Committed virtual memory = 6,910 Mb,
Free physical memory = 4,906 Mb,
Total physical memory = 8,192 Mb,
Free swap space = 26,079 Mb,
Total swap space = 26,079 Mb
Perm Gen memory:    88 Mb / 512 Mb    ++++++++++++
Free disk space:    89,341 Mb 

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

график памяти приложения

У меня также было java.net.SocketException: No buffer space available при попытке подключиться к SMTP-серверу или при попытке подключиться к серверам facebook.

Я использую Hibernate с пулом соединений c3p0 с этой конфигурацией:

        <property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
        <property name="hibernate.connection.url">jdbc:mysql://urldb/schema?autoReconnect=true</property>
        <property name="hibernate.connection.username">username</property>
        <property name="hibernate.dialect">org.hibernate.dialect.MySQL5InnoDBDialect</property>
        <property name="hibernate.connection.password"></property>
        <property name="connection.characterEncoding">UTF-8</property>

        <property name="hibernate.c3p0.acquire_increment">1</property>
        <property name="hibernate.c3p0.idle_test_period">300</property>
        <property name="hibernate.c3p0.timeout">5000</property>
        <property name="hibernate.c3p0.max_size">50</property>
        <property name="hibernate.c3p0.min_size">1</property>
        <property name="hibernate.c3p0.max_statement">0</property>
        <property name="hibernate.c3p0.preferredTestQuery">select 1;</property>
        <property name="hibernate.connection.provider_class">org.hibernate.connection.C3P0ConnectionProvider</property>

Ничего не нашел... может кто подскажет где искать?

Спасибо!

[ОБНОВЛЕНИЕ 1] HEAP DUMP:

HEAP HISTOGRAM :

class [C                                    269780  34210054
class [B                                    5600    33836661
class java.util.HashMap$Entry               221872  6212416
class [Ljava.util.HashMap$Entry;            23797   6032056
class java.lang.String                      271170  5423400
class org.hibernate.hql.ast.tree.Node       103588  4972224
class net.bull.javamelody.CounterRequest    28809   2996136
class org.hibernate.hql.ast.tree.IdentNode  23461   2205334
class java.lang.Class                       14677   2113488
class org.hibernate.hql.ast.tree.DotNode    13045   1852390
class [Ljava.lang.String;                   48506   1335600
class [Ljava.lang.Object;                   12997   1317016 


Instance Counts for All Classes (excluding platform) :

103588 instances of class org.hibernate.hql.ast.tree.Node
33366 instances of class antlr.ANTLRHashString
28809 instances of class net.bull.javamelody.CounterRequest
24436 instances of class org.apache.tomcat.util.buf.ByteChunk
23461 instances of class org.hibernate.hql.ast.tree.IdentNode
22781 instances of class org.apache.tomcat.util.buf.CharChunk
22331 instances of class org.apache.tomcat.util.buf.MessageBytes
13045 instances of class org.hibernate.hql.ast.tree.DotNode
10024 instances of class net.bull.javamelody.JRobin
9084 instances of class org.apache.catalina.loader.ResourceEntry
7931 instances of class org.hibernate.hql.ast.tree.SqlNode 

[ОБНОВЛЕНИЕ 2] server.xml:

<Connector port="8080" protocol="HTTP/1.1"
               connectionTimeout="20000"
               redirectPort="8443"
               URIEncoding="UTF-8"
               maxThreads="150"
               minSpareThreads="25"
               maxSpareThreads="75"
               enableLookups="false"
               acceptCount="1024"
               server="unknown"
               address="public_ip"
    />

****[ОБНОВЛЕНИЕ 3] Вывод из файлов журнала: ****

    2012-06-04 06:18:24,152 [http-bio-ip-8080-exec-3500] ERROR org.apache.catalina.core.ContainerBase.[Catalina].[localhost].[/api].[Jersey REST Service]- Servlet.ser
vice() for servlet [Jersey REST Service] in context with path [/socialapi] threw exception
java.net.SocketTimeoutException: Read timed out

    at java.net.SocketInputStream.socketRead0(Native Method)
    at java.net.SocketInputStream.read(SocketInputStream.java:129)
    at org.apache.coyote.http11.InternalInputBuffer.fill(InternalInputBuffer.java:532)
    at org.apache.coyote.http11.InternalInputBuffer.fill(InternalInputBuffer.java:501)
    at org.apache.coyote.http11.InternalInputBuffer$InputStreamInputBuffer.doRead(InternalInputBuffer.java:563)
    at org.apache.coyote.http11.filters.IdentityInputFilter.doRead(IdentityInputFilter.java:118)
    at org.apache.coyote.http11.AbstractInputBuffer.doRead(AbstractInputBuffer.java:326)
    at org.apache.coyote.Request.doRead(Request.java:422)

[ОБНОВЛЕНИЕ 4] ServletContext

Я использую ServletContextListener в своем приложении для создания экземпляров контроллеров и сохраняю ссылку с event.getServletContext().setAttribute. Эти контроллеры загружают конфигурации и переводы (88Mb в Перми).

Затем, чтобы использовать базу данных, которую я использую:

SessionFactory sf = dbManager.getSessionFactory(DatabaseManager.DB_KEY_DEFAULT);
Session session = sf.openSession();
Transaction tx = null; 

try {
    tx = session.beginTransaction();

    //Do stuuf

    tx.commit();

} catch (Exception e){
    //Do something
} finally {
    session.close();
}
  1. Может ли это быть источником утечки?
  2. Почему бы не использовать ручную транзакцию/сеанс, и как бы вы тогда поступили?

person Camille R    schedule 07.06.2012    source источник
comment
Когда сервер перестанет отвечать, вы также должны получить дамп потока (см. /tomcat/), чтобы увидеть, что на самом деле делает ваша JVM. Можете ли вы также опубликовать свою конфигурацию ‹Connector› из server.xml, сообщить нам о любых заброшенных функциях подключения, включенных в вашем DBCP, а также сообщить нам, каков предел дескриптора файла для вашего процесса JVM? Что-нибудь в журналах - особенно logs/catalina.out (или куда идет stdout)?   -  person Christopher Schultz    schedule 07.06.2012
comment
Обновление 2: server.xml. Хорошо, спасибо, я попробую jstack в следующий раз, когда он заблокируется! в моем списке процессов mysql я вижу соединение с ожиданием платы, ничего странного. В логах я указал это в обновлении 3   -  person Camille R    schedule 07.06.2012
comment
Ваше приложение использует много HQL? Аннотации гибернации или xml? Являются ли окончательные HQL статическими?   -  person ssedano    schedule 07.06.2012
comment
Являются ли окончательные HQL статическими? Сколько потоков требуется вашему приложению. Вы используете пул?   -  person ssedano    schedule 07.06.2012
comment
Окончательный статический HQL? Нет, я использую SessionFactory, обрабатываю сеансы и создаю запросы из сеансов и выполняю запросы в транзакциях. Поскольку он работает на tomcat, это поток на запрос. Я использую C3P0 для пула соединений Mysql, в журналах отладки все выглядит нормально.   -  person Camille R    schedule 07.06.2012
comment
Но ваши HQL уже где-то созданы? как строка?   -  person ssedano    schedule 07.06.2012
comment
да, а потом setParameters. У меня также есть 1 SQLQuery, тот же принцип.   -  person Camille R    schedule 07.06.2012
comment
Нашли где утечка?   -  person Avihai Marchiano    schedule 29.07.2013
comment
Привет! Да, это было определенно из-за подключений к базе данных и использования, мне пришлось реорганизовать способ, которым я выполнял / завершал транзакции, и тогда все работало нормально.   -  person Camille R    schedule 29.07.2013


Ответы (2)


Попробуйте с этим параметром:

+XX:+HeapDumpOnOutOfMemoryError -XX:+HeapDumpPath=dump.log

Также попробуйте с более низкими параметрами стартовой памяти -Xms.

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

Во время бега попробуй

jps

Это выведет все процессы Java, скажем, Tomcat is PID 4444:

jmap -dump:format=b,file=heapdump 4444

А также

jhat heapdump

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

Другой способ — включить статистику Hibernate, чтобы убедиться, что вы не загружаете больше объектов. Хотя похоже, что полная сборка мусора каждый час не должна быть проблемой (есть место для этого лучше).

-verbose:gc -Xloggc:/opt/tomcat/logs/gc.out -XX:+PrintGCDetails -XX:+PrintGCTimeStamps

А с помощью GCViewer, например, взгляните на каждое пространство памяти (термин, эден, выживший, пермь).

Еще один удобный инструмент:

jstack 4444 > stack.txt

Это позволит получить полную трассировку стека каждого потока, работающего внутри процесса java с pid 4444.

Имейте в виду, что вам нужны привилегии, если вы запустили Tomcat как root или другого пользователя.

jps

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

Поскольку я не знаю, о чем ваше приложение (и, следовательно, я не знаю его требований), 3 миллиона экземпляров выглядят как много.

С помощью Hibernate statistics вы можете увидеть, какие классы вы создаете чаще всего.

Тогда настройка пропорций вашего eden and ternured повторного сбора мусора может быть более эффективной.

Вновь созданные объекты отправляются в Эдем. Когда он заполняется, срабатывает второстепенный gc. То, что не удалено, отправляется в место выжившего. Когда он заполняется, он переходит в состояние ternured. Полный сборщик мусора возникнет, когда ternured будет заполнен.

На этой картинке (которая неточна) я оставил в стороне String, которые стали интернированными, и файлы с отображением памяти (которые не находятся в куче). Посмотрите, какие классы вы создаете чаще всего. Интенсивное использование String может привести к быстрому заполнению перманентной завивки.

Я предполагаю, что вы делаете это, но используете управляемую фабрику сеансов, такую ​​​​как Spring (если в вашем стеке), и избегайте ручного управления транзакциями и сеансами.

Имейте в виду, что объекты удаляются из GC, когда ни один объект не ссылается на него. Поэтому, пока объект доступен в вашем приложении, объект остается.

Если ваш ServletContextListener создает экземпляры контроллеров и сохраняется в событии getServletContext. Убедитесь, что вы полностью удалили ссылку после этого, если вы сохраните ссылку, объекты не будут удалены, поскольку они все еще доступны.

Если вы управляете своими собственными транзакциями и сеансом (что нормально, если вы не можете использовать фреймворк), то вам придется заниматься обслуживанием кода и ошибками, которые, например, Spring-tx устранил и улучшил.

Лично я бы воспользовался FOSS. Но, конечно, иногда вы не можете увеличить стек.

Если вы используете Hibernate, я бы посмотрел на Spring-orm и Spring-tx для управления транзакциями и сеансом. Также взгляните на Hibernate patter Open Session In View.

person ssedano    schedule 07.06.2012
comment
спасибо, я добавлю это и проверю дамп кучи (я проверил его из javamelody, он выглядел нормально и соответствует графику: классы кучи: 4 367, экземпляры: 3 376 160, килобайты: 372 020) - person Camille R; 07.06.2012
comment
Спасибо, так как мой сервер теперь работает нормально, jstack дает бесполезную информацию, но я сделаю это, если снова произойдет зависание со скриптом. - person Camille R; 07.06.2012
comment
Но если бы спящий режим пропускал объекты, разве он не оставался бы в используемой памяти приложения (например: org.hibernate.hql.ast.tree.Node 240 128 экземпляров 11 МБ)? - person Camille R; 07.06.2012
comment
Я не имею в виду лизание спящего режима, но ваше приложение извлекает или создает ненужные объекты (подумайте о лени сбора) - person ssedano; 07.06.2012
comment
И вы думаете, что эти ненужные объекты будут просочены? Я закрываю сеанс после каждой функции, так что теоретически это невозможно? - person Camille R; 07.06.2012
comment
Я отмечаю вопрос как отвеченный, так как его решение займет довольно много времени. Спасибо за советы и подсказки! - person Camille R; 08.06.2012

Я также рекомендую вам загрузить Visual VM 1.3.3, установить все подключаемые модули и подключить его к PID Tomcat, чтобы вы могли видеть, что происходит в режиме реального времени. Зачем ждать дампа потока? Он также расскажет вам о процессоре, потоках, всех поколениях кучи, какие объекты потребляют больше всего памяти и т. д.

person duffymo    schedule 07.06.2012
comment
для этого ему нужно будет добавить параметры запуска. Также jmap сбрасывает по запросу. jvisualvm почти так же хорош и не загружается. -Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.port=9004 -Dcom.sun.management.jmxremote.ssl=false -Dcom.sun.management.jmxremote.authenticate=false - person ssedano; 07.06.2012
comment
Я попробовал YourKit (который похож на Visual VM), к сожалению, не помог мне решить проблему (я мог использовать его только в среде разработки), есть ли способ подключить его к рабочим серверам? - person Camille R; 07.06.2012
comment
Спасибо, ssedano, но это означает, что вы открываете порт JMX как общедоступный, чего я не могу себе представить. - person Camille R; 07.06.2012
comment
ну, вы всегда можете отбросить исходящий трафик на этот порт (если вы работаете локально) - person ssedano; 07.06.2012
comment
к сожалению, моя проблема возникает только в производственной среде на выделенном сервере с реальным трафиком, мой локальный сервер с тестовым трафиком не воспроизводит проблему - person Camille R; 07.06.2012