Как java реализует мультяшный шаблон для строки под капотом?
Если у вас есть два экземпляра String, и они равны, в Java они будут использовать одну и ту же память. Как это реализовано под капотом?
EDIT: мое приложение использует большое количество объектов String, многие из которых идентичны. Каков наилучший способ использования пула констант Java String, чтобы избежать создания пользовательской версии flyweight?
Ответы
Ответ 1
Посмотрите на исходный код java.lang.String
(источник для всей java api является частью JDK).
Подводя итог: String обертывает подпоследовательность a char[]
. Эта поддержка char[]
никогда не изменяется. Это достигается не утечкой и не захватом этого char[]
вне класса String
. Однако несколько Strings
могут использовать один и тот же char[]
(см. Реализация String.substring
).
Существует также механизм интернирования, как объяснялось в других ответах.
Ответ 2
Если у вас есть два экземпляра String, и они равны, в Java они будут использовать одну и ту же память
На самом деле это не 100% истинно.
Это сообщение в блоге - достойное объяснение, почему это так, и что такое String constant pool.
Ответ 3
Строковые литералы интернированы в Java, поэтому на самом деле существует только один объект String с несколькими ссылками (когда они равны, что не всегда так). Подробнее см. Статью java.net Все о intern().
Также есть хороший пример/объяснение в разделе 3.10.5 Строковые литералы JLS, которые говорят о том, когда Strings интернированы и когда они" Будем отличаться.
Ответ 4
Это не обязательно верно. Пример:
String s1 = "hello";
String s2 = "hello";
System.out.println(s1 == s2); // true
а
String s1 = new String("hello");
String s2 = new String("hello");
System.out.println(s1 == s2); // false
Теперь вторая форма обескуражена. Некоторые (включая меня) считают, что String
не должен иметь публичного конструктора. Лучшая версия выше:
String s1 = new String("hello").intern();
String s2 = new String("hello").intern();
System.out.println(s1 == s2); // true
Очевидно, вам не нужно делать это для константы String
. Это иллюстративно.
Важным моментом в этом является то, что если вы передали String
или получили один из функции, вы не можете полагаться на String
, являющийся каноническим. Каноническое Object
удовлетворяет этому равенству:
a.equals(b) == b.equals(a) == (a == b)
для не null
экземпляров a
, b,
данного Class
.
Ответ 5
Чтобы ответить на ваш отредактированный вопрос, у Sun JVM есть опция -XX:+StringCache
, которая в моем наблюдении значительно сократит объем памяти тяжелого приложения String.
В противном случае у вас есть возможность интернировать ваши строки, но я был бы осторожен в этом. Строки, которые очень большие и больше не ссылаются, по-прежнему будут использовать память для жизни JVM.
Изменить (в ответ на комментарий): я впервые узнал о опции StringCache от здесь:
-XX: + StringCache Включает кэширование общедоступных строк.
Tom Hawtin описывает некоторый тип кэширования для улучшения некоторых тестов. Мое наблюдение, когда я положил его на IDEA, состояло в том, что след памяти (после полной сборки мусора) ушел из-за отсутствия его. Он не является документированным параметром и может быть просто оптимизирован для некоторых эталонных тестов. Мое замечание состоит в том, что это помогло, но я бы не построил на нем важную систему.
Ответ 6
Две вещи, которые нужно соблюдать осторожно:
- Не используйте конструктор
new String("abc")
, просто используйте литерал "abc"
.
- Научитесь использовать intern() метод в классе String. Особенно при конкатенации строк вместе или при преобразовании массива/массива char массива /etc в строку.
intern()
возвращает всегда строки, которые объединены.
Ответ 7
Если ваши идентичные строки исходят из фиксированного набора возможных значений, то здесь вам нужно указать перечисление типа Safe. Это не только уменьшит количество строк, но и сделает более надежным применение. Все ваше приложение будет знать, что эта строка имеет прикрепленную к ней семантику, возможно, даже некоторые методы удобства.
Мои любимые оптимизации - это те, которые можно защитить, так как код лучше, а не быстрее. И 9 раз из 10, заменяя String конкретным типом, вы получаете более правильный и самодокументирующий код.