Как java реализует шаблон веса для строки под капотом? - PullRequest
17 голосов
/ 26 мая 2010

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

РЕДАКТИРОВАТЬ: мое приложение использует большое количество объектов String, многие из которых идентичны. Каков наилучший способ использования пула констант Java String, чтобы избежать создания настраиваемой реализации типа flyweight?

Ответы [ 7 ]

12 голосов
/ 26 мая 2010

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

Это на самом деле не на 100% верно.

Этот пост в блоге - достойное объяснение того, почему это так, и что такое Пул констант .

7 голосов
/ 27 мая 2010

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

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

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

6 голосов
/ 26 мая 2010

Строковые литералы интернированы в Java, поэтому на самом деле существует только один объект String с несколькими ссылками (когда они равны, что не всегда так). См. Статью java.net Все о intern () для более подробной информации.

Есть также хороший пример / объяснение в разделе 3.10.5 Строковые литералы JLS, который говорит о том, когда строки интернированы и когда они будут различаться.

4 голосов
/ 26 мая 2010

Это не обязательно правда. Пример:

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.

3 голосов
/ 26 мая 2010

Чтобы ответить на ваш отредактированный вопрос, в виртуальных машинах Sun есть опция -XX:+StringCache, которая, по моим наблюдениям, может значительно уменьшить объем памяти приложения String.

В противном случае, у вас есть возможность интернировать свои Строки, но я буду осторожен с этим. Строки, которые являются очень большими и на которые больше не ссылаются, будут все еще использовать память для жизни JVM.

Изменить (в ответ на комментарий): я впервые узнал о параметре StringCache из здесь :

-XX: + StringCache Включает кэширование часто размещаемых строк.

Tom Hawtin описывает некоторый тип кэширования для улучшения некоторых тестов. Мое наблюдение, когда я помещал это в IDEA, было то, что след памяти (после полной сборки мусора) пошел вниз по причине отсутствия этого. Это не задокументированный параметр, и, возможно, речь идет просто об оптимизации для некоторых тестов. Мое наблюдение состоит в том, что это помогло, но я бы не стал строить на его основе важную систему.

1 голос
/ 26 мая 2010

Две вещи, о которых нужно быть осторожными:

  1. Не используйте new String("abc") конструктор, просто используйте литерал "abc".
  2. Научитесь использовать intern () метод в классе String. Особенно при объединении строк вместе или при преобразовании массива char / байтового массива / etc в строку.

intern() возвращает всегда объединенные строки.

0 голосов
/ 27 мая 2010

Если ваши идентичные строки получены из фиксированного набора возможных значений, то здесь вам нужно перечисление с безопасным типом. Это не только уменьшит количество строк, но и сделает работу более надежной. Все ваше приложение будет знать, что к этой строке привязана семантика, возможно, даже некоторые удобные методы.

Моими любимыми оптимизациями всегда являются те, которые можно защитить, так как код лучше , а не просто быстрее. И в 9 случаях из 10 замена строки конкретным типом приводит к более правильному и самодокументируемому коду.

...