Где хранятся строки в JVM: в куче или в стеке
Что проверяют
Понимаете ли вы модель памяти: в стеке лежат примитивы и ссылки, объекты живут в куче, и при чём тут пул строк. Android-разработчика следом нередко спрашивают, чем ART отличается от классической JVM и почему строки в цикле собирают через StringBuilder.
Как отвечать
Короткий ответ: объект String всегда в куче. В стеке, в кадре метода, хранится только ссылка на него, если это локальная переменная.
- литералы и пул: литерал
"Hello"интернируется, одинаковые литералы указывают на один объект. В HotSpot пул строк с Java 7 находится в обычной куче, до этого был в PermGen. В Android литералы лежат в таблице строк dex-файла, и ART тоже интернирует их; new String("Hello"): всегда создаёт новый объект в куче, отдельный от пулового. Поэтому==между ними даётfalse. В Java строки сравнивают черезequals(), в Kotlin для этого есть==, а ссылочное сравнение там===;intern(): возвращает каноническую копию из пула. На практике нужен редко;- неизменяемость: любая «модификация» создаёт новый объект. Склейка в цикле через
+плодит временные объекты, поэтому длинные строки собирают вStringBuilderилиbuildString {}в Kotlin.
Для Android это важно из-за сборщика мусора: временные строки в onDraw() или в onBindViewHolder() дают лишние аллокации на каждом кадре. Увидеть их помогает профилировщик памяти в Android Studio.
Можно добавить, что оптимизирующий компилятор умеет убирать аллокацию объекта, который не выходит за пределы метода (escape analysis). Но это оптимизация, а не место хранения, и опираться на неё в ответе не стоит.
Где спотыкаются
Говорят, что строка это примитив и потому лежит в стеке. String обычный класс, а в стеке оказывается лишь ссылка на объект.
Утверждают, что пул строк вне кучи, в PermGen. Это устарело больше десяти лет назад, а в Android PermGen не было никогда.
Сравнивают строки через == в Java и получают баг, который воспроизводится через раз: литералы совпадают по ссылке, а строки из сети или из EditText уже нет.