Как можно переполнить стек и heap в Java
Что проверяют
Понимаете ли вы, что хранится в стеке потока и что в куче, и умеете ли отличать эти ошибки в проде. Следом обычно спрашивают, как искать причину OutOfMemoryError и стоит ли его перехватывать.
Как отвечать
Стек есть у каждого потока, в нём лежат фреймы вызовов с локальными переменными и ссылками. Размер задаёт -Xss, на 64-битном Linux по умолчанию 1 МБ. Переполнить его можно так:
- бесконечная рекурсия без базового случая: классика, итог
StackOverflowError; - слишком глубокая корректная рекурсия: обход дерева или связного списка на сотни тысяч уровней;
- неявная рекурсия: двунаправленные связи, где
toString(),equals()илиhashCode()вызывают друг друга. Типичный случай: Lombok@Dataна JPA-сущностях с@OneToManyи@ManyToOneили Jackson, сериализующий цикл.
Куча общая для всех потоков, в ней живут объекты. Максимум задаёт -Xmx, без него JVM обычно берёт четверть доступной памяти, причём в контейнере учитывается лимит cgroup (регулируется -XX:MaxRAMPercentage). Переполнение даёт OutOfMemoryError, и в сообщении важна причина:
Java heap space: объекты не помещаются. Причины: кэш без вытеснения, статическая коллекция, которая только растёт,ThreadLocalв пуле потоков, вся выборка из базы в памяти;GC overhead limit exceeded: сборщик почти всё время работает и почти ничего не освобождает;Metaspace: утечка классов, часто при горячих передеплоях или генерации прокси;unable to create native thread: кончилась не куча, а потоки ОС или память под их стеки.
Для разбора включают -XX:+HeapDumpOnOutOfMemoryError, снимают дамп через jcmd <pid> GC.heap_dump и смотрят его в Eclipse MAT: кто удерживает больше всего памяти.
Где спотыкаются
Называют это «переполнением хипа» по аналогии с C. В Java нет выхода за границу буфера в куче, есть исчерпание памяти.
Лечат OOM простым увеличением -Xmx. При утечке это только отодвигает падение, а паузы GC растут.
Ловят OutOfMemoryError через catch (Throwable) и продолжают работу. После него состояние приложения непредсказуемо, правильнее упасть с дампом и перезапуститься.