JavaRush /Java блог /Java Developer /Как связаны контейнеры и Java?
Павел
11 уровень

Как связаны контейнеры и Java?

Статья из группы Java Developer
Для тех, кто почитал про контейнеры и Docker, но не понимает каким боком к контейнерам прицепилась Java. Сначала освежим свою память про память Java. Напомню, что память состоит из Stack и Heap, которые используют RAM (оперативную память) сервера, говоря дальше про память - будем иметь ввиду именно RAM. А теперь рассмотрим контейнер в разрезе. Как связаны контейнеры и Java? - 1 Нет конечно, огузок и подбедрышек нас не интересует, мы будем смотреть на структуру памяти в контейнере. Ее можно разделить на три части: • Heap Memory – собственно куча; • Off Heap – это все что не в куче; • OS Overhead – это накладные расходы памяти, на реализацию процессов внутри контейнера. Как связаны контейнеры и Java? - 2 Допустим: Мы выделили на контейнер 1 Gb, на рисунке это будет Container Limit – его границы обозначены синим прямоугольником. На память Java в контейнере мы выделили 80%, то есть 0,8 Gb и Heap занял у нас примерно 80% от контейнера, то есть не много меньше 0,8 Gb, потому что часть ресурсов забрал себе OS Overhead (накладные расходы) на поддержание процессов. Около 20% контейнера осталось на все что связано не с кучей (Off Heap). Область Used на рисунке показывает используемую область памяти на выполнение работы приложения. Теперь надо поговорить о ситуациях, когда память в контейнере может закончиться. OutOfMemoryError Если потребление памяти приложением (область Used) достигнет пределов кучи (Heap) мы словим OutOfMemoryError (OOM Error). Которое говорит, что не хватает место в куче, а именно, в области памяти, в которую помещаются объекты, создаваемые программно в приложении. Как связаны контейнеры и Java? - 3 Если еще не совсем понятно, то объясню на котиках. OOM Error - это когда кот, долго-долго орет перед дверями на балкон, а когда эту дверь откроют, то он стоит в проеме и не проходит ни туда ни сюда, часто говорят что котик завис. Если его вовремя подтолкнуть, то он отвиснет и пойдет на балкон по своим котячим делам. Как связаны контейнеры и Java? - 4 OOM Killer Это еще одна ситуация, которая может случиться в контейнере, когда заканчивается память Как связаны контейнеры и Java? - 5 Если программа залезет за пределы контейнера, то получим OutOfMemory Killer (OOM Killer) - это процесс, который завершает приложение, чтобы спасти ядро от сбоя. Он жертвует приложением, чтобы сохранить работу контейнера. Контейнер не факт что упадет, но приложение, которое в нем работает упадет точно. Такое бывает если оставить потребление памяти Java приложением без контроля. Опять на кошках. Если вы решили в субботу подольше поспать и забыли насыпать котикам еды, то OOM Killer цветочкам обеспечен, горшки может и не пострадают, а вот цветы придется сажать заново. Как связаны контейнеры и Java? - 6 Какая разница между OOM Error и OOM Killer? OOM Error вы можете обработать и совершить какие-то действия (например: масштабировать приложение), а OOM Killer просто убьет весь процесс. OOM Killer аналогичен kill-9 (kill минус девять) – команда, которая убивает процесс в Linux. Как связаны контейнеры и Java? - 7 Все дело в том, что самой популярной реализацией контейнера, является Docker контейнер, который базируется на Linux, даже если запустить его под Windows, все равно ядро будет от Linux. У Linux нам интересно одно понятие: CGroups (англ. control group) — механизм ядра Linux, который ограничивает и изолирует вычислительные ресурсы (процессорные, сетевые, ресурсы памяти, ресурсы ввода-вывода) для групп процессов. Просто говоря, этот механизм позволяет в контейнере управлять ресурсами, в нашем случае памятью. Как с этим связана Java? Именно механизмом CGroups Java может зацепиться за память контейнера. Но это все зависит от версии Java. Например, Java 7, не умеет пользоваться CGroups, и лимиты контейнера ей неизвестны. По умолчанию maximal heap size = ¼ физической памяти. Если приложение превысит лимиты контейнера, то будет OOM Killer, а если лимиты контейнера не установлены, то приложение будет забирать память у других приложений на сервере (лимиты лучше поставить, а то лягут все). Можно, конечно, использовать настройки heap или использовать специальные «образы», которые решают эту проблему, но легче всего использовать правильную версию Java. Правильные версии начинаются с Java 8 x131 (портирована с Java 9), она начинает понимать CGroups. А у же в Java 10 появился support для контейнеров: UseContainerSupport, позднее эта функция была портирована в Java 8 x 191. Или можно просто использовать Java: 11+. Какой можно сделать вывод: При использовании памяти контейнера можно получить OutOfMemoryError (OOM Error) или OutOfMemoryKiller (OOM Killer). При первом, приложение сразу не упадет, OOM Error можно поймать, обработать и совершить управляемые действия. Например масштабировать приложение. Если же случился OOM Killer, то приложение упадет сразу, никаких вариантов по его спасению не останется. И самое не приятное, что внешне сам контейнер будет в порядке, то есть можно даже не подозревать что там что то упало. Для взаимодействия с памятью контейнера и Java используются Linux механизмы. Но не всякая Java их реализует. Что бы не было проблем для Java 8 нужно использовать версию начиная от 131, а лучше от 191. Или использовать Java: 11+. Для практики: OutOfMemoryError: поймай, если сможешь
Комментарии
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ