← Назад в блог

Экономика памяти runtime · 18 июля 2026 г.

Память Java в контейнерах: почему простаивающее приложение всё ещё стоит гигабайты

Использованная и committed heap, metaspace, direct buffers, стеки потоков, page cache и RSS контейнера — разные числа. Объясняем, что означает график памяти, почему сборщик мусора может сохранять ёмкость и как гибкий биллинг RAM превращает запас в расходы.

14 мин чтенияДокументированоСмоделировано

Методика

Что именно сравнивается

  1. Разделяем used, committed и maximum JVM heap, RSS процесса и память cgroup.
  2. Моделируем непрерывно работающий 30 дней сервис; опубликованная цена памяти Railway используется только как прозрачный пример стоимости.
  3. Считаем временные ряды памяти иллюстративными формами нагрузки, а не универсальным сравнением Java и Node.
  4. Оцениваем производительность вместе со стоимостью, потому что уменьшение heap может обменять RAM на CPU сборщика, latency и риск OOM.

Heap used — не счёт контейнера

Модель MemoryUsage Oracle различает init, used, committed и max. Committed memory гарантирована JVM и всегда не меньше used. JVM может запрашивать и освобождать память со временем, но неиспользованная committed-ёмкость — не то же самое, что утечка недостижимых объектов.[1]

Процесс также владеет метаданными классов, JIT-кодом, стеками потоков, direct byte buffers, нативными библиотеками, аренами allocator и отображёнными страницами. Графики контейнера и платформы обычно показывают процесс или cgroup ближе к RSS, куда входят не одни живые Java-объекты.[2][6]

Первый график моделирует резидентный процесс на 2,8 ГБ после burst. Категории намеренно видимы, чтобы читатель мог заменить их доказательствами Native Memory Tracking, JMX, cgroup и профилировщика реального приложения.

Расчётные компоненты резидентной памяти после нагрузкиИллюстративный Java-процесс с суммарным RSS 2,8 ГБ. Компоненты не складываются во всех инструментах мониторинга, поэтому реальные измерения должны использовать одну границу учёта.
Расчётные компоненты резидентной памяти после нагрузкиИллюстративный Java-процесс с суммарным RSS 2,8 ГБ. Компоненты не складываются во всех инструментах мониторинга, поэтому реальные измерения должны использовать одну границу учёта.Живой heap800 МБОбъекты, оставшиеся после GCСвободный committed heap1200 МБДоступен JVM для быстрого повторного использованияDirect / native buffers300 МБСеть, сжатие, библиотекиMetaspace и JIT220 МБМетаданные классов и скомпилированный кодСтеки потоков160 МБЗависит от числа потоков и размера стекаRuntime / allocator120 МББиблиотеки и накладные расходы allocatorМБ

JVM выбирает размер относительно границы

Актуальная документация Java определяет доступную память как меньшее из физической памяти и ограничения окружения, например контейнера. MaxRAMPercentage задаёт долю этой основы для heap; Oracle документирует значение по умолчанию 25 процентов для этой опции в Java 25.[3]

Вручную завышенный -Xmx может оставить слишком мало места native memory. Очень маленький heap способен увеличить частоту сборки и хвостовую latency. Контейнер без лимита может поглощать память узла и стать более вероятной целью OOM kill; контейнер выше лимита может быть реактивно завершён при давлении памяти.[3][4][5]

Поэтому лимит ресурса, потолок JVM heap, число потоков, потолок direct memory и скорость аллокаций нагрузки образуют одну конфигурацию. Копирование heap-флага с большого bare-metal сервера в PaaS-контейнер на 1 ГБ не нейтрально.

Сигналы памяти и вопрос, на который отвечает каждый
СигналЧто измеряетПолезный вопросЧастая ошибка
Heap used после GCЖивые Java-объектыРастёт ли сохраняемое состояние приложения?Называть любой высокий RSS утечкой heap
Heap committedПамять, гарантированная JVM heapСколько ёмкости удерживается для повторного использования?Считать свободный committed heap бесплатным
Скорость аллокацийСоздаваемые байты в секундуНасколько интенсивно работает GC?Смотреть только на статичный снимок heap
Native memoryMetaspace, код, стеки, buffers, библиотекиЧто существует вне heap?Ставить Xmx равным лимиту контейнера
RSS / working set контейнераРезидентная память по учёту ядраКакую границу тарифицирует или применяет платформа?Сравнивать напрямую с heap used
Паузы GC и CPUСтоимость сборкиПовредил ли меньший heap latency?Оптимизировать RAM без SLA

Память после всплеска — не обязательно утечка

JVM может сохранять committed heap после падения трафика, потому что будущие аллокации дёшево используют его повторно. Oracle предоставляет MaxHeapFreeRatio, MinHeapFreeRatio и управление постепенным сжатием heap, предупреждая, что меньшие значения могут обменять footprint на производительность, а результат зависит от приложения.[3]

У утечки другой рисунок: нижняя граница после GC растёт между сопоставимыми циклами, цепочки удержания объектов увеличиваются или native allocations не возвращаются. Один sawtooth, установившийся выше старта, доказывает лишь прогрев runtime.

Временная шкала ниже иллюстративна. Значения Java и Node — не vendor benchmark; они показывают, почему запуск, пик, момент сразу после нагрузки и установившееся состояние должны входить в capacity-отчёт.

Иллюстративный RSS до, во время и после всплескаРасчётная резидентная память двух runtime с разными реализациями одной формы сервиса. Используйте как список измерений, а не сравнение языков.
Иллюстративный RSS до, во время и после всплескаРасчётная резидентная память двух runtime с разными реализациями одной формы сервиса. Используйте как список измерений, а не сравнение языков.

Гибкая цена памяти делает запас видимым

Railway сейчас публикует цену RAM $10 за ГБ в месяц измеренного использования. Поэтому сервис, устанавливающийся на 4 ГБ вместо 2 ГБ, несёт примерно $20 дополнительной месячной стоимости памяти ещё до реплик, баз и трафика. Другие платформы тарифицируют выделенные уровни, но экономический вопрос тот же.[7]

Не настраивайте только по idle. Проведите фазы запуска, steady, burst, интенсивных аллокаций и восстановления под точным лимитом cgroup. Записывайте OOM, рестарты, throughput, CPU, p95/p99 latency, паузы GC и heap после GC.

Месячная стоимость памяти по публичной цене RailwayПростая 30-дневная иллюстрация по опубликованной цене $10/ГБ/месяц. CPU, минимальный план, реплики, storage, egress и налоги исключены.
Месячная стоимость памяти по публичной цене RailwayПростая 30-дневная иллюстрация по опубликованной цене $10/ГБ/месяц. CPU, минимальный план, реплики, storage, egress и налоги исключены.В среднем 1 ГБ$10В среднем 2 ГБ$20В среднем 4 ГБ$40В среднем 8 ГБ$80USD/месяц

Реестр источников

Спецификации и цены меняются. Ссылки делают снимок проверяемым.

Источники и коммерческие данные проверены 2026-07-18. Если источник не говорит обратного, цены указаны без налогов.

  1. MemoryUsageOracle · продукт
  2. On-heap and off-heap memoryOracle · продукт
  3. The java command and advanced runtime optionsOracle · продукт
  4. Assign memory resources to containersKubernetes · продукт
  5. Resource management for Pods and containersKubernetes · продукт
  6. process.memoryUsageNode.js · продукт
  7. Railway pricingRailway · цены
  8. JVM metricsMicrometer · наблюдаемость