Где в памяти JVM располагается объект строки, созданный через new String("hello world")
Что проверяют
Знаете ли вы, что пул строк давно живёт в куче, и сколько объектов на самом деле создаёт эта строка кода. Следом почти всегда идут «сколько объектов создано?» и «что вернёт == с литералом?».
Как отвечать
Здесь участвуют два объекта String:
- литерал
"hello world": строка из пула. JVM создаёт и интернирует её при разрешении константы класса, в HotSpot лениво, при первом выполнении инструкцииldc. Если такой литерал уже встречался, берётся готовый объект; - результат
new: всегда новый объект в обычной куче. При поколенческом сборщике вроде G1 он обычно попадает в Eden, чаще всего в TLAB потока.
Сам String Pool с Java 7 не отдельная область вроде PermGen. Это хэш-таблица StringTable в нативной памяти JVM, а строки, на которые она ссылается, лежат в той же куче и собираются GC, когда ссылок не остаётся. В Java 8 PermGen заменили на Metaspace, но строки туда не переехали.
Деталь про содержимое: с Java 9 строка хранит byte[] value и признак кодировки coder (compact strings, Latin-1 занимает байт на символ). Конструктор String(String original) не копирует массив, а берёт ссылку на массив исходной строки. Два объекта String делят один массив байтов.
Отсюда поведение: new String("hello world") == "hello world" даёт false, equals даёт true, а intern() вернёт ссылку на объект из пула. Сам конструктор new String(String) в прикладном коде почти не нужен.
Где спотыкаются
Отвечают «строка лежит в PermGen» или «в String Pool вне кучи». Это описание Java 6 и старше.
Говорят, что new проверяет пул и кладёт туда литерал. Литерал попадает в пул через ldc, а конструктор про пул ничего не знает.
Уверенно отвечают «ровно два объекта». Точнее так: при первом выполнении создаются два String и один общий массив, при повторном только один новый String, потому что литерал уже в пуле.