Что происходит с объектом при удалении ссылки на него?
Есть три строки кода:
String s = null;
s = "Hellow";
s = null;
- Создали переменную
sтипаString, которая ни на что не ссылается (хранит пустую ссылкуnull). - Создаем объект-строку
Hellow, переменнаяsхранит ссылку на объект. - Удаляем из переменной
sссылку на объект.
Вопрос: что происходит со строкой Hellow после удаления ссылки на неё? Остаётся ли она в памяти или удаляется вместе со ссылкой?
Ответы (4 шт):
В Java за удаление неиспользуемых объектов отвечает сборщик мусора (анг. garbage collector). Он автоматически вызывается JVM в определённые моменты времени и удаляет те объекты, которые в данный момент в приложении не используются.
Однако в примере, приведённом вами, сборщик мусора не удалит строку, хоть вы и удалили ссылку на неё и более не используете. Дело в том, что строки в Java, создаваемые посредством кавычек, а не конструкторов, помещаются в так называемый пул строк (анг. string pool), который сборщиком мусора не затрагивается. Когда вы делаете так
s = "Hellow";
вы создаёте строку, которая будет помещена в пул строк. И даже если после этого s перестанет указывать на неё
s = null;
строка "Hellow" никогда не будет собрана сборщиком мусора, она останется в памяти, то есть, в пуле строк. Поэтому если далее сделать так
String s2 = "Hellow";
System.out.println("Hellow" == s2);
на экран будет выведено true. Потому что мы не создали новую строку, а указали на имеющуюся в пуле строк.
Однако если создавать строки посредством конструкторов
String s = new String("Hellow");
а затем затирать ссылки на них
s = null;
то в таком случае строки будут удаляться сборщиком мусора, как и любые обыкновенные объекты. И, например, такой код
String s2 = new String("Hellow");
System.out.println(new String("Hellow") == s2);
выведет на экран false, так как в обоих случаях создаются две независимые друг от друга строки "Hellow", находящиеся в разных местах памяти и при этом не находящиеся в пуле строк. Такие строки при затирании ссылок на них будут удаляться сборщиком мусора.
В данном сценарии строковый литерал "Hellow" определён в момент компиляции и соответственно будет размещён в пуле строк (string pool).
В старых версиях (Java 6 и раньше) пул строк являлся частью постоянной области памяти PermGen, которая не относилась к основной куче (Heap) и имела постоянный размер, который нельзя было увеличить.
Сборка мусора запускается только при условии, что существующая память исчерпана. Как правило, вероятность такого сценария для постоянной области памяти пренебрежимо мала -- нужно было бы или загружать множество классов или постоянно добавлять в пул новые строки при помощи String::intern. В худшем случае можно было бы добиться и OutOfMemoryError.
Начиная с Java 7 и выше пул строк размещается в куче (heap), для которой сборка мусора вызывается чаще (по сравнению с PermGen), при этом неиспользуемые строки удаляются из пула, освобождая память.
При необходимости размер кучи может увеличиваться, в том числе и превышая объём доступной физической памяти.
что происходит со строкой "Hellow" после удаления ссылки на неё? Остаётся ли она в памяти или удаляется вместе со ссылкой?
Строка останется в памяти (в данном случае, в пуле строк) до тех пор, пока не будет вызван сборщик мусора или не завершится программа.
Все вспоминают про то, что в java учетом используемых ссылок и подчисткой неиспользованной памяти занимается не программист, а сборщик мусора, но забывают, что и выделением памяти тоже занимается не программист. В приведенном фрагменте оптимизирующий компилятор сразу распознает паттерн присваивания без использования результата и не будет создавать строку и выполнять присваивание, так как результат от этого не изменится.