Когда в ОС выделяется куча jvm
Одна из наших систем sap (PI ABAP+JAVA stack) давала проблемы с производительностью. Все 64 ГБ, сконфигурированные для машины, перегружены (а также 8 ядер). Каждый подозревает Java-часть, но я думаю по-разному.
Узлы java-сервера перезапускаются с ошибкой Out Of Memory. Глядя на файлы hprof, я обнаружил, что они имеют размер всего 1,2 ГБ (в среднем на 3 серверных узла), когда для узлов сервера настроено 3 ГБ (как -Xms, так и Xmx) кучи. Это наблюдение приводит к следующему сомнению.
Я читал, что когда Xms и Xmx установлены на одно и то же значение, jvm выделяется вся куча при запуске. В этом случае серверные узлы будут иметь 3 ГБ кучи с самого начала. Если так, почему это не отражается в файле hprof или если hprof содержит только память, выделенную объектам во время выполнения, то размер явно указывает на то, что куча памяти была свободна (более 50%), поэтому ошибка OOM...!!..??
Я также знаю, что Linux делает то, что называется избыточной фиксацией памяти. т.е. память фактически не дается, когда ее запрашивают, но когда она фактически используется. Это способствует исключению нехватки памяти. Например, когда JVM запускается, операционная система сообщает, что вам выделено 3 ГБ памяти, но фактически откладывает ее до тех пор, пока она действительно не потребуется. К тому времени, когда jvm фактически пытается выделить память для объектов, некоторые другие приложения могли бы исчерпать память. Это возможно...??
Даже если бы у java-узлов была проблема утечки памяти, она не ограничивалась 3 ГБ кучи. Как это может поглотить всю 64G физической памяти....???
Еще одна вещь, которую я заметил, была то, что пространство подкачки использовалось только на 50%.
Любой свет на это...!
2 ответа
SAP OSS также изучал проблему. Сегодня я получил ответ от них. Мое наблюдение было правильным. Ява не была виновником. Стек ABAP столкнулся с некоторой проблемой и не освободил память. После перезапуска рабочего процесса ABAP память освободилась на уровне ОС.
Но я также хотел бы получить понимание по выделенной части вопроса, например, может ли такая ситуация произойти или нет, что приведет к ошибкам JAVA OOM...??..!!. Любая информация на этот счет будет полезна.
Overcommit по умолчанию включен в Linux в эвристическом режиме. Это означает, что ядро обычно допускает чрезмерную загрузку, то есть обещает больше памяти всем процессам, запрашивающим его, чем оно фактически может доставить, в надежде, что процессы никогда не начнут использовать всю память одновременно. Возможно, на вашем сервере отключен overcommit, вы можете проверить это, выполнив:
$ cat /proc/sys/vm/overcommit_memory
Если значение равно 0, эвристический overcommit включен.
Если возникает ситуация, когда фактическое использование памяти увеличивается по сравнению с объемом оперативной памяти, которую может предоставить система, ядро активирует OOM killer, который попытается уничтожить процессы, чтобы освободить память. Это обычно убивает самые молодые процессы, потребляющие большие объемы оперативной памяти, но вы не можете зависеть от этого. Это может (и будет) вызвать хаос. Вы можете изменить сходство OOM, чтобы убить определенные процессы, отрегулировав /proc//oom_adj (например, если вы хотите избежать ситуации, когда OOM убивает базу данных или другого большого пользователя RAM [ab]).
Таким образом, если ваша система переходит в фазу OOM, последствия для процессов Java могут заключаться в том, что они мгновенно уничтожаются - что не приведет к выводу сообщений "Недостаточно памяти" в журналах Java, которые вы наблюдаете.
Установка одинаковых значений для Xmx и Xms предотвратит изменение размера кучи, но это не означает, что процесс java при запуске начнет использовать всю память сразу. Он будет выделять столько, сколько ему нужно памяти VIRT, но резидентный набор данных не увеличится до Xms, но останется настолько низким, насколько это необходимо.
С точки зрения виртуальной памяти: ядро пообещает (сверхкоммитировать) Java-процесс столько, сколько ему требуется (Xmx + некоторое дополнительное), но вся эта память не будет выделена немедленно. Сумма, необходимая для текущих данных, будет распределена только, и вы можете увидеть, сколько, наблюдая размер резидентного набора (не поменялась местами физическая память, используемая задачей). Чтобы увидеть размеры VIRT и RSS, вы можете выполнить следующую команду:
$ ps aux | egrep '(^USER|java)'
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
tomcat 10229 21.5 9.1 6813688 548344 ? Sl 09:01 1:10 ....java...
По всей вероятности, ошибки, которые вы наблюдаете, указывают на то, что программе, выполняющейся в процессе виртуальной машины Java, не хватает места в куче. Попробуйте увеличить значение Xmx и повторно протестировать свое приложение.