Если у Вас есть два экземпляра Строки, и они равны в Java, они совместно используют ту же память. Как это реализовано под капотом?
Править: Мое приложение использует большое количество Строковых объектов, многие из которых идентичны. Что лучший способ состоит в том, чтобы использовать пул Строковой константы Java, чтобы не создавать пользовательскую реализацию в наилегчайшем весе?
Посмотрите исходный код java.lang.String
(исходный код всего java api является частью JDK).
Подводя итог: String является оболочкой для подпоследовательности char []
. Эта поддержка char []
никогда не изменяется. Это не достигается ни утечкой, ни захватом этого char []
вне класса String
. Однако несколько строк
могут совместно использовать один и тот же char []
(см. Реализацию String.substring
).
Существует также механизм интернирования, как объясняется в других ответах.
Если у вас есть два экземпляра String, и они равны, в Java они будут использовать одну и ту же память
На самом деле это не на 100% верно.
Эта статья в блоге хорошо объясняет, почему это так, и что такое пул констант String.
Две вещи, с которыми нужно быть осторожным:
new String("abc")
, используйте только литерал "abc"
. intern()
всегда возвращает объединенные строки.
Строковые литералы интернированы в Java, поэтому на самом деле существует только один объект String с несколькими ссылками (когда они равны, что не всегда так). См. Статью java.net Все о intern () для получения более подробной информации.
Также есть хороший пример / объяснение в разделе 3.10.5 Строковые литералы JLS, в котором говорится о том, когда строки интернированы и когда они будут отличаться.
Это не обязательно правда. Пример:
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
является каноническим ]. канонический объект
удовлетворяет этому равенству:
a.equals(b) == b.equals(a) == (a == b)
для не нулевых
экземпляров a
, b,
данного Класс
.
Чтобы ответить на ваш отредактированный вопрос, в Sun JVM есть опция -XX:+StringCache
, которая, по моим наблюдениям, может значительно уменьшить объем памяти приложения с большим количеством строк.
В противном случае, у вас есть возможность интернализации строк, но я был бы осторожен в этом вопросе. Строки, которые очень велики и на которые больше не ссылаются, будут использовать память в течение всей жизни JVM.
Редактировать (в ответ на комментарий): Я впервые узнал об опции StringCache из здесь:
-XX:+StringCache Enables caching of commonly allocated strings.
Tom Hawtin описывает некоторый тип кэширования для улучшения некоторых эталонных показателей. По моим наблюдениям, когда я включил этот параметр в IDEA, объем памяти (после полной сборки мусора) значительно уменьшился по сравнению с отсутствием этого параметра. Это не документированный параметр, и, возможно, это просто оптимизация для некоторых бенчмарков. По моим наблюдениям, он помог, но я бы не стал строить на его основе важную систему.
Если ваши одинаковые строки происходят из фиксированного набора возможных значений, то безопасное для типов перечисление - это то, что вам нужно. Это не только сократит количество строк, но и сделает приложение более надежным. Все ваше приложение будет знать, что у этой строки есть семантика, привязанная к ней, возможно, даже некоторые удобные методы.
Мои любимые оптимизации - это те, которые можно защитить как делающие код лучше, а не просто быстрее. И 9 раз из 10 замена String на конкретный тип приводит к более корректному и самодокументированному коду.