Сравните reserved и committed память JVM: что означает ситуация, когда committed заметно меньше reserved?
Reserved — это зарезервированный JVM диапазон виртуальных адресов, а committed — часть ресурсов, которую JVM уже закрепила за собой для возможного использования. Если committed меньше reserved, это обычно означает нормальное поэтапное расширение памяти: адресное пространство подготовлено заранее, но физическая память или соответствующие страницы ОС ещё не выделены полностью.
Такой подход появился из-за различия между виртуальным адресным пространством процесса и физической памятью. JVM нужно было управлять большими областями памяти, не требуя немедленно выделять под них весь объём RAM.
Разделение резервирования и подтверждения выделения позволяет запускать приложения с заранее заданным верхним пределом памяти и увеличивать фактическое потребление по мере необходимости. Это особенно важно для heap, metaspace и некоторых областей нативной памяти.
Неверная интерпретация этих показателей приводит к ошибочным выводам о нехватке памяти. Например, большое значение reserved ещё не означает, что процесс уже занял такой объём RAM, а маленькое значение committed не гарантирует низкий RSS.
Также нельзя автоматически считать committed полностью физически resident-памятью. ОС может выделять страницы лениво, а JVM и операционная система учитывают разные категории памяти.
При резервировании JVM получает диапазон виртуальных адресов. Этот диапазон становится недоступным для других назначений внутри процесса, но физические страницы под весь диапазон могут ещё отсутствовать.
При commit JVM сообщает, что часть диапазона готова к использованию. Для неё могут быть зарезервированы ресурсы ОС, однако фактические физические страницы нередко создаются только при первом обращении к ним из-за механизма demand paging.
Важно отличать ещё один показатель — used. Он показывает, сколько памяти фактически занято объектами или метаданными с точки зрения конкретного компонента JVM. Поэтому для одной области возможна картина: reserved больше committed, а committed больше used.
Для диагностики нужно смотреть на несколько уровней одновременно:
Эти значения не обязаны совпадать. Кроме того, heap, стеки потоков, библиотеки, direct memory и внутренние структуры JVM могут выделяться разными механизмами и не всегда одинаково отражаются в стандартных метриках.
Сервис запускается с большим максимальным heap. Мониторинг показывает, что reserved почти равен заданному максимуму, committed значительно меньше, а RSS постепенно растёт во время нагрузки.
Первый вариант — считать reserved фактическим потреблением. Это даёт ложное срабатывание: резервирование адресного пространства само по себе не означает расход RAM.
Второй вариант — анализировать только used heap. Он может пропустить рост памяти за пределами объектов: metaspace, direct buffer, стеки потоков и служебные структуры JVM.
Третий вариант — сопоставить показатели heap с RSS и данными Native Memory Tracking, если его включение допустимо по накладным расходам. Это позволяет отделить рост живых объектов от commit-операций и нативных потребителей.
Практически выбирают третий вариант: reserved используют для оценки адресных ограничений, committed и used — для анализа состояния конкретной области, а RSS и нативные метрики — для объяснения фактического потребления процесса. Такой разбор показывает, растёт ли heap, неуправляемая память или лишь виртуальное адресное пространство.
1. Вопрос: Может ли committed быть больше RSS?
Да, в зависимости от платформы, JVM и способа учёта памяти. Committed означает готовность области к использованию JVM, но не является прямым эквивалентом resident-физической памяти: страницы могут быть выделены лениво, выгружены или учитываться ОС иначе. Поэтому сравнение committed и RSS нужно интерпретировать с учётом конкретной области памяти и операционной системы.
2. Вопрос: Означает ли уменьшение used после GC немедленное уменьшение RSS?
Нет. Сборщик может освободить объекты, уменьшив used, но JVM способна сохранить полученные страницы в своих пулах для последующих аллокаций. Кроме того, возврат страниц операционной системе зависит от сборщика, режима работы JVM и конкретной области памяти. Поэтому после GC used может снизиться, а committed и RSS останутся почти неизменными.
3. Вопрос: Почему большой reserved иногда всё равно опасен?
Резервирование занимает виртуальное адресное пространство. На 64-битных системах его обычно много, но оно не бесконечно и может конфликтовать с другими отображениями памяти, особенно при большом числе областей или нативных библиотек. Кроме того, reserved задаёт потенциальный масштаб роста: если commit быстро увеличится, процесс может столкнуться с лимитом контейнера, RAM или swap, хотя изначально фактический RSS был небольшим.