Что такое интернирование строк (String interning) в Java?

Ответ

Интернирование строк — это механизм в Java, при котором все строковые литералы и строки, явно добавленные в пул, хранятся в специальной области heap памяти, называемой String Pool (пул строк). Его цель — экономия памяти и повышение производительности за счёт повторного использования неизменяемых (immutable) строковых объектов.

Как это работает:

  • При создании строкового литерала ("text") JVM сначала ищет строку с таким же значением в пуле.
  • Если строка найдена, возвращается ссылка на существующий объект. Если нет — создаётся новый объект в пуле.
  • Строки, созданные через оператор new String(...), по умолчанию не интернируются и размещаются в обычной heap-памяти.

Пример:

String s1 = "hello";          // Литерал -> попадает в пул
String s2 = "hello";          // Находит "hello" в пуле -> s1 == s2 (true)
String s3 = new String("hello"); // Создаётся новый объект вне пула
String s4 = s3.intern();       // Явное интернирование, s4 теперь ссылается на объект из пула

System.out.println(s1 == s2); // true (одна ссылка)
System.out.println(s1 == s3); // false (разные объекты)
System.out.println(s1 == s4); // true (s4 после intern() ссылается на объект из пула)

Метод String.intern():

  • Помещает строку в пул, если её там ещё нет, и возвращает ссылку на строку из пула.
  • Если строка уже есть в пуле, возвращает существующую ссылку.

Важные особенности:

  • Пул строк находится в Heap (ранее — в Permanent Generation, до Java 7).
  • Строки в пуле могут быть удалены сборщиком мусора, если на них нет ссылок.
  • Использовать intern() стоит с осторожностью, так как неконтролируемое наполнение пула может привести к излишнему потреблению памяти (утечке). Оптимально использовать для долгоживущих строк с высоким шансом повторения.

Ответ 18+ 🔞

А, интернирование строк! Ну это ж классика, блядь, как "Муму" у Тургенева, только про байты и хеш-коды, а не про собак. Сейчас объясню на пальцах, чтобы даже мартышлюшка с ушами поняла.

Представь себе, что строка в Java — это как Герасим, здоровый мужик, но немой. Он может сказать только "Муму". И таких Герасимов, которые мычат одно и то же, может быть дохуя. Зачем плодить сущностей, блядь? Вот для этого и придумали String Pool — этакий общий сарай, куда складывают каждого уникального Герасима по одному разу.

Как эта магия работает, ёпта:

  • Пишешь ты String s1 = "hello"; — JVM, хитрая жопа, лезет в этот сарай-пул, смотрит: а есть у нас уже Герасим, который мычит "hello"? Нету? Ну, блядь, создаём нового и в сарай его. Есть? О, сука, отлично! Возвращаем тебе ссылку на того, который уже там стоит, сэкономили память, молодцы.
  • А вот если ты через new String("hello") создаёшь — это как взять и построить Герасима-дублера прямо посреди поля. Он внешне такой же, мычит так же, но живёт в другом месте. И ссылки у вас будут разные, пиздец.

Смотри, как это в коде выглядит, тут всё честно:

String s1 = "hello";          // Сарай (пул). Герасим №1.
String s2 = "hello";          // Идём в сарай, находим того же Герасима №1. s1 и s2 — одно лицо!
String s3 = new String("hello"); // А это новый Герасим №2, но на вольных хлебах, вне сарая.
String s4 = s3.intern();       // Говорим Герасиму №2: "Иди в сарай, блядь!" Он идёт, видит, что его клон уже там, и начинает тыкать пальцем в того, первого.

System.out.println(s1 == s2); // true (Оба пальцем тыкают в одного и того же Герасима в сарае)
System.out.println(s1 == s3); // false (Один в сарае, другой по полю гуляет — разные объекты!)
System.out.println(s1 == s4); // true (s4 после intern() тоже тыкает в сарайного Герасима)

Метод intern() — это, блядь, как команда "В сарай!" Крикнул intern() — и строка либо залетает в пул (если её там не было), либо, если её дубль уже там сидит, просто начинает на него ссылаться. Всё, чтобы не плодить одинаковых мычащих Герасимов по всему heap'у.

А теперь, внимание, ебать мои старые костыли, важные детали:

  • Пул строк сейчас живёт в обычной куче (Heap), а не где-то на небесах. Раньше, до Java 7, его в Permanent Generation пихали, но там тесно стало.
  • Да-да, Герасимов из сарая могут выкинуть, если на них никто не тычет пальцем. Сборщик мусора и там рулит.
  • С intern() надо как с ножом — осторожно, ёпта. Начать бездумно пихать в пул всё подряд — это гарантированно получить овердохуища одинаковых строк и утечку памяти. Используй это дело только для тех строк, которые реально будут жить долго и встречаться чаще, чем пидары на халявную раздачу. Иначе сарай лопнет, и будет тебе пиздец, а не оптимизация.

Вот и вся философия. Литералы — сразу в сарай. new — гуляй, Вася. Захотел оптимизации — кричи intern() и отправляй в сарай. Главное — не переборщить, а то вместо быстрой программы получишь памятник Герасиму, который всех замусорил.