Сбор мусора струнных литералов
Я читаю о сборке мусора, и я получаю запутывающие результаты поиска, когда я ищу коллекции строковых литералов String.
Мне нужно уточнить следующие пункты:
-
Если строка во время компиляции определяется как литерал [например: String str = "java"
], будет ли это сбор мусора?
-
Если используется метод intern [например: String str = new String("java").intern()
], будет ли он собираться мусор? Также будет обрабатываться иначе, чем строковый литерал в точке 1.
-
В некоторых местах упоминается, что литералы будут собирать мусор только тогда, когда класс String
будет выгружен? Имеет ли смысл, потому что я не думаю, что класс String
когда-нибудь будет выгружен.
Ответы
Ответ 1
Если строка во время компиляции определяется как литерал [например: String str = "java";
], будет ли это сбор мусора?
Наверное, нет. Объекты кода будут содержать одну или несколько ссылок на объекты String
, которые представляют литералы. До тех пор, пока объекты кода достижимы, объекты String
будут.
Возможно, объекты кода становятся недоступными, но только если они динамически загружены... и их загрузчик классов уничтожен.
Если я использую метод intern (например: String str = new String("java").intern()
), будет ли это сбор мусора?
Объект, возвращаемый вызовом intern
, будет тем же самым объектом, который представляет строковый литерал "java"
. (Литерал "java"
интернирован во время загрузки класса. Когда вы затем ставите новый объект String
в фрагменте кода, он будет искать и возвращать ранее интернированную строку "java"
.)
Однако интернированные строки, которые не идентичны строковым литералам, могут быть собраны в мусор, как только они становятся недоступными. Пространство PermGen - это мусор, собранный на всех последних JVM HotSpot. (До Java 8... который полностью отбрасывает PermGen.)
Также он будет обрабатываться иначе, чем строковый литерал в точке 1.
Нет... потому что это тот же объект, что и строковый литерал.
И действительно, как только вы поймете, что происходит, ясно, что строковые литералы тоже не рассматриваются. Это просто применение правила "достижимости"...
В некоторых местах упоминается, что литералы будут собирать мусор только тогда, когда класс String
будет выгружен? Это имеет смысл, потому что я не думаю, что класс String
будет выгружен.
Вы правы. Это не имеет смысла. Источники, которые сказали, что неверны. (Было бы полезно, если бы вы разместили URL-адрес, чтобы мы могли прочитать, что они говорят для себя...)
Ответ 2
При нормальных обстоятельствах строковые литералы и классы распределяются в постоянное поколение JVM ( "PermGen" ) и обычно не собираются. Строки, которые интернированы (например, mystring.intern()
), хранятся в пуле памяти, принадлежащем классу String
в пермгене, и когда-то случай, когда агрессивное интернирование могло вызвать утечку пространства, потому что сам пул строк содержал ссылку на каждый string, даже если других ссылок не существует. По-видимому, это уже не так, по крайней мере, от JDK 1.6 (см., Например, здесь).
Для получения дополнительной информации о permgen, этот является достойным обзором темы. (Примечание: эта ссылка относится к блогу, связанному с продуктом. У меня нет связи с блогом, компанией или продуктом, но запись в блоге полезна и не имеет большого отношения к продукту. )
Ответ 3
- Литеральная строка останется в памяти до тех пор, пока программа находится в памяти.
-
str
будет собран мусор, но литерал, из которого он создан, не будет.
- Это имеет смысл, так как класс строки выгружается, когда программа выгружается.
Ответ 4
intern()
проверяет доступность объекта в пуле строк. Если объект/литерал доступен, ссылка будет возвращена. Если литерала нет в пуле, тогда объект загружается в области perm (пул строк), а затем ссылка на него будет возвращена. Мы должны разумно использовать метод intern()
.