Всем привет, капнем сегодня немного глубже: в JVM и пройдем по ее памяти.



Большинство знает, что JVM можно конфигурировать, используя различные флаги, но почему -Xmx2g не означает, что Java-процесс съест максимум 2 ГБ

Часто при настройке Java-приложения мы пишем:
-Xms2g
-Xmx2g
И возникает логичная мысль:

«Отлично. JVM будет использовать максимум 2 ГБ памяти».

И вот это совсем не так, -Xmx2g ограничивает только Java Heap.

Процесс JVM состоит далеко не из одного Heap.

Упрощённо память выглядит так:
Java Process
│
├── Heap
│ ├── Young Generation
│ └── Old Generation
│
├── Metaspace
├── Thread Stacks
├── Code Cache
├── Direct Memory
└── Native Memory

Например, приложение действительно может иметь:
-Xmx2g

но при этом процесс спокойно занимать больше 2 ГБ.

Почему?

1. Heap - здесь находятся обычные Java-объекты:
User user = new User();
List<Order> orders = new ArrayList<>();
Именно размер Heap ограничивает -Xmx.

2. Metaspace - здесь JVM хранит метаданные загруженных классов.
Например:

class User {
...
}

Сам объект User находится в Heap, а информация о самом классе User — в Metaspace.

Поэтому можно получить ситуацию:
Heap → 2 GB
Metaspace → 300 MB
и процесс уже использует больше двух гигабайт.

3. Stack каждого потока - у каждого Java-потока есть собственный Stack.
Например:
-Xss1m
Если приложение создало 500 потоков, потенциально это уже сотни мегабайт адресного пространства только под стеки.
Именно поэтому «давайте создадим побольше потоков» — не бесплатная операция.

4. Direct Memory - например, Netty активно использует off-heap память.
Она не обязана находиться в Java Heap:
Heap
↓
Java objects

Direct Memory
↓
native/off-heap buffers

Поэтому приложение может иметь вполне нормальный Heap:
Heap: 1.5 GB / 2 GB
и при этом продолжать потреблять память за пределами этих 2 ГБ.

Допустим, Kubernetes-контейнер имеет лимит:
Memory limit = 2 GB
А Java запускается с:
-Xmx2g
На первый взгляд всё прекрасно, но JVM получает возможность занять 2 ГБ только Heap, а ей ещё нужны Metaspace, стеки потоков, native memory, direct buffers и другие области.

В результате:
Container limit
↓
2 GB
│
├── Heap 2 GB
├── Metaspace
├── Threads
├── Direct
└── Native
И приложение может получить: OOMKilled даже несмотря на то, что Heap ещё не достиг своего лимита, поэтому в production недостаточно сказать:

«У нас Xmx = 2g, значит приложению нужно 2 ГБ памяти».

Нужно смотреть на память всего процесса, и это одна из причин, почему настройка JVM — это не: -Xmx = размер контейнера.
Просмотры: 35
Открыть в MAX Сообщить о неточности

Репосты публикации

Публикация канала

Сообщить о неточности

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

Вы покидаете datool.ru

Ссылка ведёт на внешний сайт. datool.ru не отвечает за его содержимое — проверьте адрес, прежде чем вводить пароли или платёжные данные.

Переход через 10 с

Перейти