
Одна из наших систем 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 ГБ кучи. Как она может захватить все 64 ГБ физической памяти...???
Еще одна вещь, которую я заметил, — пространство подкачки было использовано только на 50%.
Есть ли какой-нибудь свет на это...!
решение1
SAP OSS также изучала эту проблему. Сегодня я получил от них ответ. Мои наблюдения были верны. Java не была виновником. Стек ABAP столкнулся с какой-то проблемой и не освобождал память. После перезапуска рабочего процесса ABAP память была освобождена на уровне ОС.
Но я также хотел бы получить понимание по выделенной части вопроса, например, может ли возникнуть такая ситуация или нет, приводящая к ошибкам JAVA OOM...??..!!. Любая информация по этому поводу будет полезна.
решение2
Overcommit по умолчанию включен в Linux в эвристическом режиме. Это означает, что ядро обычно разрешает overcommit, то есть обещает больше памяти всем процессам, запрашивающим ее, чем оно может предоставить на самом деле, в надежде, что процессы никогда не начнут использовать всю память одновременно. Возможно, overcommit отключен на вашем сервере, вы можете проверить это, выполнив:
$ cat /proc/sys/vm/overcommit_memory
Если значение равно 0, эвристическое превышение лимита включено.
Если возникнет ситуация, когда фактическое использование памяти превысит объем оперативной памяти, который может предоставить система, ядро активирует OOM killer, который попытается завершить процессы, чтобы освободить память. Обычно он завершает самые молодые процессы, потребляющие большие объемы оперативной памяти, но на него нельзя положиться. Он может (и будет) вызывать хаос. Вы можете изменить привязку OOM для завершения определенных процессов, настроив /proc//oom_adj (например, если вы хотите избежать ситуации, когда OOM завершает работу базы данных или какого-либо другого пользователя с большим объемом оперативной памяти [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 и повторно протестируйте свое приложение.