Подтвердить что ты не робот

Процесс Tomcat, который был убит ядром Linux после истечения времени подкачки; не получается ошибка JVM OutOfMemory

Я выполнял тестирование нагрузки на сервере tomcat. На сервере имеется физическая память 10G и пространство подкачки 2G. Размер кучи (xms и xmx) был установлен до 3G раньше, и сервер просто работал нормально. Так как я все еще видел много свободной памяти, и производительность не была хорошей, я увеличил размер кучи до 7G и снова проверил нагрузочное тестирование. На этот раз я заметил, что физическая память была съедена очень быстро, и система начала потреблять пространство подкачки. Позже, tomcat потерпел крах после того, как закончил свободное пространство. Я включил -XX:+HeapDumpOnOutOfMemoryError при запуске tomcat, но я не получил кучу кучи. Когда я проверил /var/log/messages, я увидел kernel: Out of memory: Kill process 2259 (java) score 634 or sacrifice child.

Чтобы предоставить дополнительную информацию, вот что я увидел из команды Linux top, когда размер кучи установлен на 3G и 7G

xms & xmx = 3G (который работал нормально):

  • Перед запуском tomcat:

    Mem:  10129972k total,  1135388k used,  8994584k free,    19832k buffers
    Swap:  2097144k total,        0k used,  2097144k free,    56008k cached
    
  • После запуска tomcat:

    Mem:  10129972k total,  3468208k used,  6661764k free,    21528k buffers
    Swap:  2097144k total,        0k used,  2097144k free,   143428k cached
    PID  USER      PR  NI  VIRT  RES  SHR S %CPU %MEM    TIME+  COMMAND
    2257 tomcat    20   0 5991m 1.9g  19m S 352.9 19.2   3:09.64 java
    
  • После запуска нагрузки в течение 10 минут:

    Mem:  10129972k total,  6354756k used,  3775216k free,    21960k buffers
    Swap:  2097144k total,        0k used,  2097144k free,   144016k cached
    PID  USER      PR  NI  VIRT  RES  SHR S %CPU %MEM    TIME+  COMMAND
    2257 tomcat    20   0 6549m 3.3g  10m S 332.1 34.6  16:46.87 java
    

xms & xmx = 7G (что вызвало крах tomcat):

  • Перед запуском tomcat:

    Mem:  10129972k total,  1270348k used,  8859624k free,    98504k buffers
    Swap:  2097144k total,        0k used,  2097144k free,    74656k cached
    
  • После запуска tomcat:

    Mem:  10129972k total,  6415932k used,  3714040k free,    98816k buffers
    Swap:  2097144k total,        0k used,  2097144k free,   144008k cached
    PID  USER      PR  NI  VIRT  RES  SHR S %CPU %MEM    TIME+  COMMAND
    2310 tomcat    20   0  9.9g 3.5g  10m S  0.3 36.1   3:01.66 java
    
  • После запуска нагрузки в течение 10 минут (прямо перед тем, как был убит tomcat):

    Mem:  10129972k total,  9960256k used,   169716k free,      164k buffers
    Swap:  2097144k total,  2095056k used,     2088k free,     3284k cached
    PID  USER      PR  NI  VIRT  RES  SHR S %CPU %MEM    TIME+  COMMAND
    2310 tomcat    20   0 10.4g 5.3g  776 S  9.8 54.6  14:42.56 java
    

Версия Java и JVM:

Java(TM) SE Runtime Environment (build 1.7.0_21-b11)
Java HotSpot(TM) 64-Bit Server VM (build 23.21-b01, mixed mode)

Версия Tomcat:

6.0.36

Сервер Linux:

Red Hat Enterprise Linux Server release 6.4 (Santiago)

Итак, мои вопросы:

  • Почему эта проблема произойдет? Когда JVM исчерпывает память, почему нет OutOfMemoryError? И почему это прямо идет на использование свопа?
  • Почему top RES показывает, что java использует память 5.3G, там больше потребляемой памяти?

Я изучал и искал какое-то время, но не могу найти основную причину этой проблемы. Большое спасибо!

4b9b3361

Ответ 1

Почему эта проблема произойдет? Когда JVM исчерпывает память, почему не выбрано OutOfMemoryException?

Это не из-за нехватки памяти JVM. Это Операционная система хоста, которая исчерпала ресурсы, связанные с памятью, и принимает решительные меры. ОС не знает, что процесс (в данном случае JVM) способен отключиться упорядоченным образом, когда ему сообщают "Нет" в ответ на запрос на большее количество памяти. Он должен что-то убить, иначе существует серьезная опасность повсюду висит ОС.

Во всяком случае, причина, по которой вы не видите OOME, заключается в том, что это не ОЧЕНЬ ситуация. На самом деле, JVM уже уделяет слишком много памяти ОС, и нет способа вернуть ее. Это проблема, с которой ОС сталкивается с трудными процессами убийства.

И почему это прямо идет на использование swap?

Он использует swap, потому что общая потребность в виртуальной памяти всей системы не будет вписываться в физическую память. Это нормальное поведение для операционной системы UNIX/Linux.

Почему верхний RES показывает, что java использует память 5.3G, там больше потребляемой памяти

Номера RES могут немного ввести в заблуждение. То, на что они ссылаются, - это объем физической памяти, который в данный момент использует этот процесс... исключая материал, который является общим или общим для других процессов. Номер VIRT более важен для вашей проблемы. Он говорит, что ваша JVM использует 10.4g виртуальных... что больше, чем доступная физическая память в вашей системе.


Как говорится в другом ответе, это касается того, что это касается вас, что вы не получаете OOME. Даже если вы его получили, было бы неразумно делать что-либо с этим. OOME может нанести сопутствующий ущерб вашему приложению/контейнеру, который трудно обнаружить и с которого сложно восстановить. Поэтому OOME является Error не a Exception.


Рекомендации:

  • Не пытайтесь использовать значительно больше виртуальной памяти, чем у вас есть физическая память, особенно с Java. Когда JVM запускает полную сборку мусора, она будет касаться большинства ее страниц VM, несколько раз в случайном порядке. Если вы значительно перераспределили свою память, это может вызвать избиение, которое убивает производительность для всей системы.

  • Увеличивайте пространство подкачки системы. (Но это может не помочь...)

  • Не пытайтесь восстановить из OOME.

Ответ 2

У вас, вероятно, есть другие процессы на том же компьютере, которые также используют память. Похоже, ваш Java-процесс достигает около 5,3 ГБ, прежде чем машина отчаянно выходит из ОЗУ и меняет их. (Другие процессы, вероятно, используют 12GB-5.3GB = 6.7GB). Таким образом, ваше linux-ядро жертвует ваш Java-процесс, чтобы поддерживать другие процессы. Предел java-памяти никогда не достигается, поэтому вы не получаете исключение OutOfMemoryException.

Рассмотрите все процессы, которые должны выполняться на всей машине, и соответствующим образом скорректируйте настройку Xmx (чтобы оставить место для всех других процессов). Возможно, 5gb?

В любом случае, подсчет исходящих OutOfMemoryExceptions является довольно плохим запахом кода. Если я правильно помню, получение даже одного OutOfMemoryException может оставить JVM в состоянии "все-ставки-выключено" и, вероятно, должно быть перезапущено, чтобы не стать неустойчивым.