Где хранятся значения статических атрибутов класса в JVM
Что проверяют
Знаете ли вы, как устроена память HotSpot в современных версиях, а не пересказываете схему времён PermGen. Это вопрос с ловушкой: популярный ответ «в Metaspace» неверный. Следом обычно спрашивают, может ли статическое поле стать причиной утечки памяти.
Как отвечать
Сначала разведите понятия. Спецификация JVM описывает логическую область method area и не говорит, где физически лежат статические поля, это деталь реализации. В HotSpot картина такая:
- Metaspace: нативная память вне кучи. Там метаданные класса: структура
Klass, байткод методов, constant pool, описания полей. Значений статических полей там нет; - куча: значения статических полей хранятся в куче, внутри объекта
java.lang.Classэтого класса (его называют mirror). Так стало с Java 7, когда статику вынесли из PermGen, а в Java 8 PermGen убрали совсем; - ссылочное поле: в mirror лежит ссылка, а объект, на который она указывает, обычный объект в куче;
- стек: только локальные переменные и параметры конкретного вызова, к статике отношения не имеет.
Дальше про время жизни. Статические поля живут, пока жив класс, а класс выгружается только вместе со своим загрузчиком, когда тот стал недостижим. Отсюда классическая утечка: статический кеш, который только растёт, или ссылка из статики, которая держит загрузчик веб-приложения после редеплоя в сервере приложений.
Полезно добавить про инициализацию: статический инициализатор (<clinit>) выполняется не при загрузке класса, а при первом активном использовании, и JVM гарантирует, что он отработает один раз и потокобезопасно. На этом держится идиома Holder для ленивого синглтона. Константы вида static final int со значением времени компиляции javac вообще подставляет в байткод вызывающих классов.
Где спотыкаются
Отвечают «в Metaspace» или «в PermGen». Первое неверно для значений, второго нет с Java 8.
Говорят, что статика хранится в стеке, путая время жизни поля и время жизни вызова метода.
Считают, что сборщик статические поля не трогает никогда. Пока класс жив, они служат корнями, но после выгрузки класса освобождается и то, на что они ссылались.