Как происходит очистка памяти в String Pool?

Когда сборщик мусора решает очистить наш String Pool от лишних значений и почему он делает это не так быстро, что делает конкатенацию строк дурным тоном?
  • 16 января 2024 г. 21:38
Ответы на вопрос 2
В Java String Pool (также известный как intern pool) является особым хранилищем строк в Java Heap, где JVM хранит строковые литералы. Этот механизм позволяет экономить память, так как одинаковые строковые литералы хранятся в памяти только один раз.

Очистка памяти в String Pool происходит так же, как и в других частях кучи, с помощью сборщика мусора (Garbage Collector, GC). Строки в пуле имеют обычные ссылки, и если на строку больше нет активных ссылок (то есть если строка не достижима), GC может удалить ее, освобождая память для повторного использования.

Почему сборщик мусора не очищает String Pool быстро и почему конкатенация строк может быть дурным тоном:
1. Конкатенация строк:
Когда строки конкатенируются (например, с использованием оператора `+`), каждый раз создается новый объект String. Во время исполнения кода это может привести к созданию множества временных и, возможно, ненужных объектов String, которые потом необходимо собирать GC.

```java
String s = "Hello";
s += " ";
s += "World";
```

В примере выше создается минимум три объекта строки: `"Hello"`, `"Hello "` и `"Hello World"`. Если такой код выполняется в цикле или в часто вызываемом методе, количество временных объектов растет, что может увеличить нагрузку на сборщика мусора.

2. Оптимизация GC:
Сборщики мусора в JVM обычно оптимизированы таким образом, чтобы откладывать очистку памяти до момента, когда это действительно необходимо, чтобы снизить влияние на производительность приложения. Это означает, что объекты могут оставаться в памяти даже после того, как они станут недостижимы, до следующего цикла сборки мусора.

3. Поколенческий сбор мусора:
В некоторых сборщиках мусора, таких как G1 и CMS, используется поколенческий подход, который разделяет кучу на различные "поколения". Молодые поколения собираются чаще, а старшее поколение — реже. String Pool обычно находится в старшем поколении (PermGen до Java 7, Metaspace начиная с Java 8), поэтому очистка этой области происходит менее часто.

Чтобы избежать чрезмерного создания временных объектов при конкатенации строк, рекомендуется использовать классы `StringBuilder` или `StringBuffer` для манипуляций со строками:

```java
StringBuilder sb = new StringBuilder("Hello");
sb.append(" ");
sb.append("World");
String s = sb.toString();
```

Таким образом, вы оптимизируете использование памяти и сокращаете нагрузку на сборщик мусора.
str создает еще одну запись и ссылается на последнее значение

Зануда мод: не переменная что-то создает, а JVM помещает новую строку в пул, если использовать создание строки литералом, а не конструктором. А потом ссылку на выделенную область памяти помещает в str

В какой момент сборщик мусора решает очистить наш String Pool от лишних значений и почему он это делает не так быстро, что конкатенации строки считается дурным тоном?

До Java 6 String Pool жили в отдельной области памяти, которую GC не чистят (т.е. единожды объявленная строка жила до завершения программы, даже без ссылок). Начиная с 7-ки пулл перенесли в основную кучу, где его может почистить GC. Не не забываем, что и сам гарбадж коллектор не сферический конь, а имеет с десяток реализаций, в каждой из которых есть свои настройки - не копал эту тему, но подозреваю, что можно настроить "неприкосновенность" String Pool даже в общей куче (но зачем?)

Вторая часть вопроса бессмысленна. Забудь про строки. Пусть у тебя есть некий класс А. Твой вопрос эквивалентен следующему: Почему считается дурным тоном создавать множественные объекты класса А и каждый из них присваивать в одну и ту же переменную? Это медленно и расточительно к ресурсам!
Похожие вопросы