Подтвердить что ты не робот

Как java реализует мультяшный шаблон для строки под капотом?

Если у вас есть два экземпляра String, и они равны, в Java они будут использовать одну и ту же память. Как это реализовано под капотом?

EDIT: мое приложение использует большое количество объектов String, многие из которых идентичны. Каков наилучший способ использования пула констант Java String, чтобы избежать создания пользовательской версии flyweight?

4b9b3361

Ответ 1

Посмотрите на исходный код java.lang.String (источник для всей java api является частью JDK).

Подводя итог: String обертывает подпоследовательность a char[]. Эта поддержка char[] никогда не изменяется. Это достигается не утечкой и не захватом этого char[] вне класса String. Однако несколько Strings могут использовать один и тот же char[] (см. Реализация String.substring).

Существует также механизм интернирования, как объяснялось в других ответах.

Ответ 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 конкретным типом, вы получаете более правильный и самодокументирующий код.