Это хорошая практика, чтобы использовать java.lang.String.intern ()?

194

Javadoc о String.intern()не дает много подробностей. (В двух словах: он возвращает каноническое представление строки, позволяя сравнивать интернированные строки, используя ==)

  • Когда я буду использовать эту функцию в пользу String.equals()?
  • Есть ли побочные эффекты, не упомянутые в Javadoc, то есть более или менее оптимизация JIT-компилятором?
  • Есть ли дальнейшее использование String.intern()?
Даниэль Риковски
источник
14
Вызов intern () оказывает свое влияние на производительность, поэтому необходимо протестировать использование intern () для повышения производительности, чтобы убедиться, что оно действительно значительно ускоряет вашу программу и оправдывает дополнительную сложность. Вы также можете использовать это, чтобы уменьшить потребление памяти для больших таблиц с относительно повторяющимися значениями. Однако в обоих случаях есть другие варианты, которые могут быть лучше.
Питер Лори
Да, intern () имеет свое влияние на производительность. Тем более, что стоимость intern () увеличивается линейно, когда вы интернируете строки и сохраняете ссылку на них. По крайней мере, на солнце / оракул 1.6.0_30 вм.
lacroix1547

Ответы:

125

Когда бы я использовал эту функцию в пользу String.equals ()

когда ты нуждаешься скорость, так как вы можете сравнивать строки по ссылке (== быстрее чем равно)

Есть ли побочные эффекты, не упомянутые в Javadoc?

Основным недостатком является то, что вы должны помнить, чтобы убедиться, что вы на самом деле выполняете intern () для всех строк, которые вы собираетесь сравнивать. Легко забыть интернировать () все строки, и тогда вы можете получить до смешного неверные результаты. Кроме того, ради всех, пожалуйста, убедитесь, что очень четко задокументировали, что вы полагаетесь на интернализируемые строки.

Вторым недостатком, если вы решите интернализировать строки, является то, что метод intern () является относительно дорогим. Он должен управлять пулом уникальных строк, поэтому он выполняет большую часть работы (даже если строка уже была усвоена). Итак, будьте осторожны в своем дизайне кода, чтобы вы, например, интернировали () соответствующие строки на входе, чтобы вам больше не пришлось об этом беспокоиться.

(от Дж.Гуру)

Третий недостаток (только Java 7 или менее): внутренние строки живут в пространстве PermGen, которое обычно довольно мало; Вы можете столкнуться с OutOfMemoryError с большим количеством свободного места в куче.

(от Майкла Боргвардта)

DFA
источник
64
Третий недостаток: интернированные строки живут в пространстве PermGen, которое обычно довольно мало; Вы можете столкнуться с OutOfMemoryError с большим количеством свободного места в куче.
Майкл Боргвардт
15
Более новые виртуальные машины AFAIK также собирают мусор в пространстве PermGen.
Даниэль Риковски
31
Стажер об управлении памятью, а не о скорости сравнения. Разница между if (s1.equals(s2))и if (i1 == i2)минимальна, если у вас много длинных строк с одинаковыми ведущими символами. В большинстве реальных применений (кроме URL) строки будут отличаться в пределах первых нескольких символов. И все же длинные цепочки if-else являются запахом кода: используйте перечисления и функторные карты.
kdgregory
25
вы все еще можете использовать синтаксис s1.equals в своей программе, НЕ используйте ==, .equals используйте == внутренне для оценки короткого замыкания
gtrak
15
Майкл Боргвардт НЕ сказал, что интернированные строки нельзя собирать. И это ЛОЖНОЕ утверждение. То, что комментарии Майкла (правильно) говорят, является более тонким, чем это.
Стивен С.
193

Это (почти) не имеет ничего общего со сравнением строк. Интернирование строк предназначено для экономии памяти, если в вашем приложении много строк с одинаковым содержимым. При использовании String.intern()приложения в долгосрочной перспективе будет иметься только один экземпляр, и побочным эффектом является то, что вы можете выполнять быстрое сравнение равенств ссылок вместо обычного сравнения строк (но это обычно не рекомендуется, потому что его действительно легко сломать, забыв про только стажера) один экземпляр).

Даниэль Брюкнер
источник
4
Это не правильно. Интернирование строк происходит всегда, автоматически, когда оценивается каждое строковое выражение. Всегда есть одна копия для каждой уникальной строки используемых символов, и она «внутренне используется», если происходит многократное использование. Вызов String.intern () не делает все это возможным - он просто возвращает внутреннее каноническое представление. Смотри Javadoc.
Глен Бест
16
Необходимо уточнить - интернирование всегда происходит автоматически для константных строк времени компиляции (литералы и фиксированные выражения). Кроме того, это происходит, когда String.intern () вызывается во время выполнения динамически оцениваемых строк.
Глен Бест
Таким образом, вы имеете в виду, что если в куче 1000 объектов «Hello» и я выполняю intern () для одного из них, то остальные 999 объектов будут уничтожены автоматически?
Арун Радж
@ArunRaaj нет, у вас все еще будет 1000 в куче, и еще один в пуле стажеров, который может быть готов к повторному использованию позже, str.intern()когда strбудет "Hello".
Матье
37

String.intern()это определенно мусор, собранный в современных JVM.
Следующее НИКОГДА не заканчивается из-за активности GC:

// java -cp . -Xmx128m UserOfIntern

public class UserOfIntern {
    public static void main(String[] args) {
        Random random = new Random();
        System.out.println(random.nextLong());
        while (true) {
            String s = String.valueOf(random.nextLong());
            s = s.intern();
        }
    }
}

Смотрите больше (от меня) о мифе о не GCed String.intern () .

Гили Нахум
источник
26
OutOfMemoryException- нет, не код выше, в моем мозгу : ссылка на javaturning статью, которая указывает на эту статью, которая указывает на javaturning статью, которая ... :-)
user85421
Хотя вы можете видеть, что сообщение было отредактировано, чтобы добавить эту ссылку;)
Riking
3
Вы можете упомянуть, что вы являетесь автором внешней ссылки, на которую вы ссылаетесь.
Турбьёрн Равн Андерсен
11
@Carlos, связывающий внешнюю ссылку, которая ссылается на stackoverflow, должен вызвать .. Stackoverflow :)
Seiti
2
@Seiti Циркулярные ссылки легко обнаружить в эти дни: p
Ajay
16

Недавно я написал статью о реализации String.intern () в Java 6, 7 и 8: String.intern в Java 6, 7 и 8 - пул строк .

Я надеюсь, что он должен содержать достаточно информации о текущей ситуации с пулами строк в Java.

В двух словах:

  • Избегайте String.intern()в Java 6, потому что это входит в PermGen
  • Предпочитают String.intern()в Java 7 и Java 8: он использует в 4-5 раз меньше памяти, чем собственный пул объектов
  • Обязательно настройтесь -XX:StringTableSize(по умолчанию, вероятно, слишком мало; установите простое число)
mik1
источник
3
Пожалуйста, не просто размещайте ссылки на свой блог, это рассматривается некоторыми как СПАМ. Плюс ссылки в блоге имеют заметную тенденцию к смерти 404 смерти. Пожалуйста, кратко изложите свою статью здесь или оставьте эту ссылку в комментарии к вопросу.
Мат
3
Спасибо, что написали @ mik1! Очень информативная, понятная и актуальная статья. (Я вернулся сюда с намерением опубликовать ссылку на него сам.)
Люк Ашервуд
1
Спасибо за упоминание аргумента -XX. Вы также можете использовать это, чтобы увидеть статистику таблицы: -XX: + PrintStringTableStatistics
csadler
13

Сравнение строк с == намного быстрее, чем с equals ()

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

String.intern () вытащить строку из кучи и поместить ее в PermGen

Интернализованные строки помещаются в другую область хранения: постоянная генерация, которая является областью JVM, зарезервированной для не пользовательских объектов, таких как классы, методы и другие внутренние объекты JVM. Размер этой области ограничен и намного ценнее кучи. Поскольку эта область меньше, чем Heap, есть большая вероятность использовать все пространство и получить исключение OutOfMemoryException.

Строка String.intern () - сборщик мусора

В новых версиях JVM внутренняя строка также собирается мусором, когда на него не ссылается ни один объект.

Имея в виду вышеупомянутый 3 пункт, вы можете вывести, что String intern () может быть полезен только в немногих ситуациях, когда вы делаете много строк сравнения, однако лучше не использовать внутреннюю строку, если вы точно не знаете, что вы делаем ...

aleroot
источник
1
Чтобы добавить, исключения из памяти кучи иногда можно восстановить, особенно в многопоточных моделях, таких как веб-приложения. Когда permgen исчерпан, приложение, как правило, будет постоянно не функционировать и часто будет сбрасывать ресурсы до тех пор, пока не будет уничтожено.
Тейлор
7

Когда бы я использовал эту функцию в пользу String.equals ()

Учитывая, что они делают разные вещи, вероятно, никогда.

Внутренние строки по соображениям производительности, так что вы можете сравнить их по ссылочному равенству, будут полезны только в том случае, если вы удерживаете ссылки на строки некоторое время - строки, поступающие из пользовательского ввода или ввода-вывода, не будут интернированы.

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

Почти всегда лучше создать UserIdтип, который является интернированным (легко создать универсальный механизм интернирования, java.lang.Stringориентированный на многопотоковое исполнение) и действующий как открытое перечисление, чем перегружать тип ссылочной семантикой, если это происходит с идентификатором пользователя.

Таким образом, вы не получите путаницы между тем, была ли интернирована конкретная строка или нет, и вы можете инкапсулировать любое дополнительное поведение, которое вам требуется в открытом перечислении.

Пит Киркхэм
источник
6

Я не знаю о каких-либо преимуществах, и если бы они были, можно было бы подумать, что equals () сама использовала бы intern () внутри (что не так).

Разорение интерна (мифы)

объекты
источник
7
Несмотря на то, что вы говорите, что не знаете о каких-либо преимуществах, ваши опубликованные ссылки идентифицируют сравнение с помощью == как 5-кратное ускорение и, следовательно, важное значение для тексто-ориентированного кода исполнения
Brian Agnew
3
Когда у вас будет много возможностей для сравнения текста, вам в конечном итоге не хватит места в PermGen. Когда не так много сравнения текста, разница в скорости не имеет значения. В любом случае, просто не интернируйте () ваши строки. Это того не стоит.
Бомба
Далее также говорится, что общий относительный выигрыш, как правило, будет небольшим.
объекты
Я не думаю, что такая логика действительна. Хорошая ссылка, хотя!
Даниэль Риковски
1
@DR: какая логика? Это одна большая ошибка. @objects: извините, но ваши аргументы не соответствуют причинам. Есть очень веские причины для использования internи очень веские причины, которые equalsпо умолчанию этого не делают. Ссылка, которую вы разместили, полная чушь. Последний абзац даже признает, что internимеет допустимый сценарий использования: обработка тяжелого текста (например, анализатор). Заключение о том, что «[XYZ] опасно, если вы не знаете, что делаете», настолько банально, что это причиняет физический вред.
Конрад Рудольф
4

Даниэль Брюкнер абсолютно прав. Интернирование строк предназначено для экономии памяти (кучи). Наша система в настоящее время имеет гигантскую хэш-карту для хранения определенных данных. По мере масштабирования системы хэш-карта будет достаточно большой, чтобы создать кучу памяти (как мы уже тестировали). Благодаря интернированию всех дублированных строк и всех объектов в хэш-карте, это экономит нам значительный объем пространства кучи.

Также в Java 7, интернированные строки больше не живут в PermGen, а вместо этого кучи. Так что вам не нужно беспокоиться о его размере, и да, он получает мусор:

В JDK 7 интернированные строки больше не выделяются в постоянном поколении кучи Java, а вместо этого выделяются в основной части кучи Java (известной как молодое и старое поколения) вместе с другими объектами, созданными приложением , Это изменение приведет к увеличению объема данных, находящихся в основной куче Java, и уменьшению объема данных в постоянной генерации, что может потребовать корректировки размеров кучи. Большинство приложений увидят только относительно небольшие различия в использовании кучи из-за этого изменения, но более крупные приложения, которые загружают много классов или интенсивно используют метод String.intern (), увидят более существенные различия.

XLI
источник
Я должен подтвердить следующее: в моем программном обеспечении дамп кучи показал, что большая часть пространства кучи была использована Stringэкземплярами. Просматривая их содержание, я увидел много дубликатов и решил переключиться на них intern(), что сэкономило сотни МБ.
Матье
4

Есть ли побочные эффекты, не упомянутые в Javadoc, то есть более или менее оптимизация JIT-компилятором?

Я не знаю об уровне JIT, но есть прямая поддержка байт-кода для пула строк , которая волшебным образом и эффективно реализуется с помощью выделенной CONSTANT_String_infoструктуры (в отличие от большинства других объектов, которые имеют более общие представления).

JVMs

JVMS 7 5.1 говорит :

Строковый литерал является ссылкой на экземпляр класса String и является производным от структуры CONSTANT_String_info (§4.4.3) в двоичном представлении класса или интерфейса. Структура CONSTANT_String_info дает последовательность кодовых точек Unicode, составляющих строковый литерал.

Язык программирования Java требует, чтобы идентичные строковые литералы (то есть литералы, которые содержат одинаковую последовательность кодовых точек) должны ссылаться на один и тот же экземпляр класса String (JLS §3.10.5). Кроме того, если метод String.intern вызывается для какой-либо строки, результатом является ссылка на тот же экземпляр класса, который будет возвращен, если эта строка появится в виде литерала. Таким образом, следующее выражение должно иметь значение true:

("a" + "b" + "c").intern() == "abc"

Для получения строкового литерала виртуальная машина Java проверяет последовательность кодовых точек, заданных структурой CONSTANT_String_info.

  • Если метод String.intern ранее вызывался для экземпляра класса String, содержащего последовательность кодовых точек Unicode, идентичную той, которая задана структурой CONSTANT_String_info, то результатом строкового литерального вывода является ссылка на тот же экземпляр класса String.

  • В противном случае создается новый экземпляр класса String, содержащий последовательность кодовых точек Unicode, заданную структурой CONSTANT_String_info; ссылка на этот экземпляр класса является результатом строкового литерала. Наконец, метод intern нового экземпляра String вызывается.

Bytecode

Также полезно взглянуть на реализацию байт-кода в OpenJDK 7.

Если мы декомпилируем:

public class StringPool {
    public static void main(String[] args) {
        String a = "abc";
        String b = "abc";
        String c = new String("abc");
        System.out.println(a);
        System.out.println(b);
        System.out.println(a == c);
    }
}

у нас по постоянному пулу:

#2 = String             #32   // abc
[...]
#32 = Utf8               abc

и main:

 0: ldc           #2          // String abc
 2: astore_1
 3: ldc           #2          // String abc
 5: astore_2
 6: new           #3          // class java/lang/String
 9: dup
10: ldc           #2          // String abc
12: invokespecial #4          // Method java/lang/String."<init>":(Ljava/lang/String;)V
15: astore_3
16: getstatic     #5          // Field java/lang/System.out:Ljava/io/PrintStream;
19: aload_1
20: invokevirtual #6          // Method java/io/PrintStream.println:(Ljava/lang/String;)V
23: getstatic     #5          // Field java/lang/System.out:Ljava/io/PrintStream;
26: aload_2
27: invokevirtual #6          // Method java/io/PrintStream.println:(Ljava/lang/String;)V
30: getstatic     #5          // Field java/lang/System.out:Ljava/io/PrintStream;
33: aload_1
34: aload_3
35: if_acmpne     42
38: iconst_1
39: goto          43
42: iconst_0
43: invokevirtual #7          // Method java/io/PrintStream.println:(Z)V

Обратите внимание, как:

  • 0и 3: то же самоеldc #2 загружается одна константа (литералы)
  • 12: создается новый экземпляр строки (с #2 аргументом as)
  • 35: aи cсравниваются как обычные объекты сif_acmpne

Представление константных строк довольно волшебно в байт-коде:

  • у него есть специальная структура CONSTANT_String_info , в отличие от обычных объектов (например new String)
  • структура указывает на структуру CONSTANT_Utf8_info, которая содержит данные. Это единственные необходимые данные для представления строки.

и приведенная выше цитата JVMS, по-видимому, говорит о том, что всякий раз, когда Utf8, на который указывают, является одним и тем же, тогда загружаются идентичные экземпляры ldc.

Я сделал аналогичные тесты для полей, и:

  • static final String s = "abc"указывает на таблицу констант через атрибут ConstantValue
  • не финальные поля не имеют этого атрибута, но все еще могут быть инициализированы с ldc

Бонус : сравните это с целочисленным пулом , который не имеет прямой поддержки байт-кода (т.е. не имеет CONSTANT_String_infoаналогов).

Сиро Сантилли 郝海东 冠状 病 六四 事件 法轮功
источник
2

Я бы рассмотрел intern и == - сравнение вместо equals только в случае, когда сравнение equals является узким местом в множественных сравнениях строки. Это маловероятно, чтобы помочь с небольшим количеством сравнений, потому что intern () не является бесплатным. После агрессивного интернирования строк вызовы intern () будут становиться все медленнее и медленнее.

Микко Мауну
источник
2

Некоторая утечка памяти может возникнуть в результате использования, subString()когда результат невелик по сравнению с исходной строкой, а объект имеет длительный срок службы.

Нормальным решением является использование, new String( s.subString(...))но когда у вас есть класс, который хранит результат потенциального / вероятного subString(...)и не имеет контроля над вызывающей стороной, вы можете рассмотреть возможность сохранения intern()аргументов String, переданных конструктору. Это освобождает потенциальный большой буфер.

eremmel
источник
Интересно, но, возможно, это зависит от реализации.
Акостадинов
1
Вышеупомянутая потенциальная утечка памяти не происходит в Java 1.8 и 1.7.06 (и новее), см. Изменения во внутреннем представлении String, сделанные в Java 1.7.0_06 .
eremmel
это подтверждает, что микрооптимизации должны применяться только при необходимости после профилирования производительности и / или памяти. Спасибо.
Акостадинов
2

Строковое интернирование полезно в случае, когда equals()метод вызывается часто, потому что equals()метод быстро проверяет, совпадают ли объекты в начале метода.

if (this == anObject) {
    return true;
}

Обычно это происходит при поиске в Collectionдругом коде, но может также проверяться равенство строк.

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

Лучшее место для прохождения интернирования - обычно, когда вы читаете ключи, которые хранятся вне кода, поскольку строки в коде автоматически интернируются. Обычно это происходит на этапах инициализации вашего приложения, чтобы предотвратить наказание первого пользователя.

Другое место, где это можно сделать, - при обработке пользовательского ввода, который можно использовать для поиска ключей. Обычно это происходит в вашем обработчике запросов, обратите внимание, что интернированные строки должны быть переданы вниз.

Кроме того, нет смысла проводить интернирование в остальной части кода, поскольку это, как правило, не даст никакой выгоды.

Архимед Траяно
источник
1

Я бы проголосовал за то, чтобы он не стоил хлопот по обслуживанию.

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

wm_eddie
источник
1

http://kohlerm.blogspot.co.uk/2009/01/is-javalangstringintern-really-evil.html

утверждает, что String.equals()использует "=="для сравнения Stringобъектов раньше, в соответствии с

http://www.codeinstructions.com/2009/01/busting-javalangstringintern-myths.html

он сравнивает длины строк, а затем содержимое.

(Между прочим, строки кода продукта в каталоге продаж должны быть одинаковой длины - BIC0417 - защитный шлем велосипедиста, TIG0003 - живой тигр взрослого мужчины - вам, вероятно, понадобятся все виды лицензий, чтобы заказать одну из них. И может быть, вам лучше заказать защитный шлем в то же время.)

Таким образом, звучит так, как будто вы получаете выгоду от замены строк на их intern()версию, но вы получаете безопасность - и удобочитаемость, а также соответствие стандартам - без использования «==» для equals()вашего программирования. И большая часть того, что я собираюсь сказать, зависит от того, является ли это правдой, если это правда.

Но String.equals()проверяет ли вы, что передали ему строку, а не какой-либо другой объект, перед использованием "=="? Я не квалифицирован, чтобы сказать, но я не думаю, потому что в подавляющем большинстве таких equals()операций будет String to String, так что тест почти всегда проходит. Действительно, приоритизация «==» внутри String.equals()подразумевает уверенность в том, что вы часто сравниваете строку с одним и тем же реальным объектом.

Надеюсь, никто не удивится, что следующие строки выдают результат «false»:

    Integer i = 1;
    System.out.println("1".equals(i));

Но если вы измените iк i.toString()во второй строке, конечно , это true.

Места, где вы можете надеяться на выгоду от стажировки, включают Setи Map, очевидно. Я надеюсь, что в интернированных строках кэшируются их хэш-коды ... Я думаю, что это будет требованием. И я надеюсь, что я не просто выдал идею, которая могла бы заработать мне миллион долларов. :-)

Что касается памяти, также очевидно, что это важный предел, если у вас большой объем Strings или если вы хотите, чтобы объем памяти, используемой вашим программным кодом, был очень маленьким. Если ваш объем -distinct-Strings очень велик, возможно, пришло время рассмотреть возможность использования выделенного программного кода базы данных для управления ими и отдельного сервера базы данных. Аналогично, если вы можете улучшить небольшую программу (которая должна запускаться в 10000 экземпляров одновременно), если она вообще не хранит свои строки.

Создавать новую строку, а затем сразу же отбрасывать ее для intern()замены, кажется бесполезной , но не существует четкой альтернативы, кроме как сохранить дублирующую строку. Таким образом, на самом деле стоимость выполнения заключается в поиске вашей строки в пуле интернов, а затем в том, чтобы сборщик мусора мог удалить оригинал. И если это строковый литерал, то он все равно интернируется.

Мне интересно, intern()может ли злонамеренный программный код использоваться для обнаружения того, что некоторые строки и их ссылки на объекты уже существуют в intern()пуле и, следовательно, существуют в другом месте сеанса Java, когда это не должно быть известно. Но это возможно только тогда, когда программный код уже используется доверительно, я думаю. Тем не менее, стоит подумать о сторонних библиотеках, которые вы включаете в свою программу для хранения и запоминания своих ПИН-кодов банкоматов!

Роберт Карнеги
источник
0

Настоящая причина использовать интерна не выше. Вы можете использовать его после того, как вы получите ошибку нехватки памяти. Многие строки в типичной программе - это String.substring () другой большой строки [подумайте о том, чтобы извлечь имя пользователя из XML-файла размером 100 КБ. Реализация Java заключается в том, что подстрока содержит ссылку на исходную строку и начало + конец в этой огромной строке. (За этим стоит повторное использование одной и той же большой строки)

После 1000 больших файлов, из которых вы сохраняете только 1000 коротких имен, вы сохраняете в памяти целые 1000 файлов! Решение: в этом случае просто используйте smallsubstring.intern ()

Асаф
источник
Почему бы просто не создать новую строку из подстроки, если вам это нужно?
Турбьёрн Равн Андерсен
0

Я использую intern для экономии памяти, я храню большой объем данных String в памяти и перехожу к использованию intern (), который сэкономил огромный объем памяти. К сожалению, хотя он использует намного меньше памяти, память, которую он использует, хранится в памяти PermGen, а не в куче, и клиентам сложно объяснить, как увеличить выделение этого типа памяти.

Итак, есть ли альтернатива intern () для уменьшения потребления памяти (преимущества == по сравнению с равными для меня не проблема)

Пол Тейлор
источник
0

Давайте посмотрим правде в глаза: основной сценарий использования - это когда вы читаете поток данных (либо через входной поток, либо из JDBC ResultSet), и существует множество маленьких строк, которые повторяются повсюду.

Вот небольшой трюк, который дает вам некоторый контроль над тем, какой механизм вы хотели бы использовать для интернализации строк и других неизменяемых, и пример реализации:

/**
 * Extends the notion of String.intern() to different mechanisms and
 * different types. For example, an implementation can use an
 * LRUCache<T,?>, or a WeakHashMap.
 */
public interface Internalizer<T> {
    public T get(T obj);
}
public static class LRUInternalizer<T> implements Internalizer<T> {
    private final LRUCache<T, T> cache;
    public LRUInternalizer(int size) {
        cache = new LRUCache<T, T>(size) {
            private static final long serialVersionUID = 1L;
            @Override
            protected T retrieve(T key) {
                return key;
            }
        };
    }
    @Override
    public T get(T obj) {
        return cache.get(obj);
    }
}
public class PermGenInternalizer implements Internalizer<String> {
    @Override
    public String get(String obj) {
        return obj.intern();
    }
}

Я часто использую это, когда читаю поля из потоков или из ResultSets. Примечание: LRUCacheэто простой кеш на основе LinkedHashMap<K,V>. Он автоматически вызывает предоставленный пользователем retrieve()метод для всех пропусков кэша.

Способ использовать это - создать его LRUInternalizerперед чтением (или чтением), использовать его для интернализации строк и других небольших неизменяемых объектов, а затем освободить его. Например:

Internalizer<String> internalizer = new LRUInternalizer(2048);
// ... get some object "input" that stream fields
for (String s : input.nextField()) {
    s = internalizer.get(s);
    // store s...
}
Пьер Д
источник
0

Я использую его для кэширования содержимого примерно 36000 кодов, которые ссылаются на связанные имена. Я интернирую строки в кеше, потому что многие коды указывают на одну и ту же строку.

Интернируя строки в моем кэше, я гарантирую, что коды, которые указывают на одну и ту же строку, на самом деле указывают на одну и ту же память, тем самым экономя мое место в оперативной памяти.

Если бы интернированные строки были фактически собраны мусором, это не сработало бы для меня вообще. Это в основном сводит на нет цель интернирования. У меня не будет мусора, потому что я держу ссылку на каждую строку в кэше.

Родни П. Барбати
источник
Нет, все интернированные одинаковые строки, которые находятся в памяти в определенное время, все равно будут одним и тем же объектом. Это будет объект, отличный от той же строки, которая была в памяти перед сборкой мусора. Но это не проблема, потому что старой строки больше нет.
бдрумен
0

Стоимость интернирования строки намного больше, чем время, сэкономленное при сравнении одной строки A.equals (B). Используйте его (из соображений производительности) только в том случае, если вы постоянно используете одни и те же неизменные строковые переменные. Например, если вы регулярно перебираете стабильный список строк для обновления некоторых карт, привязанных к одному и тому же строковому полю, вы можете получить хорошее сохранение.

Я бы предложил использовать интернирование строк для настройки производительности, когда вы оптимизируете определенные части своего кода.

Также помните, что String неизменны и не делают глупую ошибку

String a = SOME_RANDOM_VALUE
a.intern()

не забудьте сделать

String a = SOME_RANDOM_VALUE.intern()
grumblebee
источник
0

Если вы ищете неограниченную замену для String.intern, а также для сборки мусора, следующее хорошо работает для меня.

private static WeakHashMap<String, WeakReference<String>> internStrings = new WeakHashMap<>();
public static String internalize(String k) {
    synchronized (internStrings) {
        WeakReference<String> weakReference = internStrings.get(k);
        String v = weakReference != null ? weakReference.get() : null;
        if (v == null) {
            v = k;
            internStrings.put(v, new WeakReference<String>(v));
        }
        return v;
    }
}

Конечно, если вы можете приблизительно оценить, сколько будет различных строк, просто используйте String.intern () с -XX: StringTableSize = highEnoughValue .

bdruemen
источник
SoftRef сделает больше смысла.
ВАЧ
@vach При использовании WeakReference (вместо SoftReference) память освобождается раньше, поэтому другие распределения могут выполняться быстрее. Это зависит от того, что еще приложение делает, любой может иметь смысл.
августа,