Javadoc о String.intern()
не дает много подробностей. (В двух словах: он возвращает каноническое представление строки, позволяя сравнивать интернированные строки, используя ==
)
- Когда я буду использовать эту функцию в пользу
String.equals()
? - Есть ли побочные эффекты, не упомянутые в Javadoc, то есть более или менее оптимизация JIT-компилятором?
- Есть ли дальнейшее использование
String.intern()
?
Ответы:
когда ты нуждаешься скорость, так как вы можете сравнивать строки по ссылке (== быстрее чем равно)
Основным недостатком является то, что вы должны помнить, чтобы убедиться, что вы на самом деле выполняете intern () для всех строк, которые вы собираетесь сравнивать. Легко забыть интернировать () все строки, и тогда вы можете получить до смешного неверные результаты. Кроме того, ради всех, пожалуйста, убедитесь, что очень четко задокументировали, что вы полагаетесь на интернализируемые строки.
Вторым недостатком, если вы решите интернализировать строки, является то, что метод intern () является относительно дорогим. Он должен управлять пулом уникальных строк, поэтому он выполняет большую часть работы (даже если строка уже была усвоена). Итак, будьте осторожны в своем дизайне кода, чтобы вы, например, интернировали () соответствующие строки на входе, чтобы вам больше не пришлось об этом беспокоиться.
(от Дж.Гуру)
Третий недостаток (только Java 7 или менее): внутренние строки живут в пространстве PermGen, которое обычно довольно мало; Вы можете столкнуться с OutOfMemoryError с большим количеством свободного места в куче.
(от Майкла Боргвардта)
источник
if (s1.equals(s2))
иif (i1 == i2)
минимальна, если у вас много длинных строк с одинаковыми ведущими символами. В большинстве реальных применений (кроме URL) строки будут отличаться в пределах первых нескольких символов. И все же длинные цепочки if-else являются запахом кода: используйте перечисления и функторные карты.Это (почти) не имеет ничего общего со сравнением строк. Интернирование строк предназначено для экономии памяти, если в вашем приложении много строк с одинаковым содержимым. При использовании
String.intern()
приложения в долгосрочной перспективе будет иметься только один экземпляр, и побочным эффектом является то, что вы можете выполнять быстрое сравнение равенств ссылок вместо обычного сравнения строк (но это обычно не рекомендуется, потому что его действительно легко сломать, забыв про только стажера) один экземпляр).источник
str.intern()
когдаstr
будет"Hello"
.String.intern()
это определенно мусор, собранный в современных JVM.Следующее НИКОГДА не заканчивается из-за активности GC:
Смотрите больше (от меня) о мифе о не GCed String.intern () .
источник
OutOfMemoryException
- нет, не код выше, в моем мозгу : ссылка на javaturning статью, которая указывает на эту статью, которая указывает на javaturning статью, которая ... :-)Недавно я написал статью о реализации String.intern () в Java 6, 7 и 8: String.intern в Java 6, 7 и 8 - пул строк .
Я надеюсь, что он должен содержать достаточно информации о текущей ситуации с пулами строк в Java.
В двух словах:
String.intern()
в Java 6, потому что это входит в PermGenString.intern()
в Java 7 и Java 8: он использует в 4-5 раз меньше памяти, чем собственный пул объектов-XX:StringTableSize
(по умолчанию, вероятно, слишком мало; установите простое число)источник
Сравнение строк с == намного быстрее, чем с equals ()
5 В разы быстрее, но поскольку сравнение строк обычно составляет лишь небольшой процент от общего времени выполнения приложения, общий выигрыш намного меньше этого, и окончательный выигрыш будет уменьшен до нескольких процентов.
String.intern () вытащить строку из кучи и поместить ее в PermGen
Интернализованные строки помещаются в другую область хранения: постоянная генерация, которая является областью JVM, зарезервированной для не пользовательских объектов, таких как классы, методы и другие внутренние объекты JVM. Размер этой области ограничен и намного ценнее кучи. Поскольку эта область меньше, чем Heap, есть большая вероятность использовать все пространство и получить исключение OutOfMemoryException.
Строка String.intern () - сборщик мусора
В новых версиях JVM внутренняя строка также собирается мусором, когда на него не ссылается ни один объект.
Имея в виду вышеупомянутый 3 пункт, вы можете вывести, что String intern () может быть полезен только в немногих ситуациях, когда вы делаете много строк сравнения, однако лучше не использовать внутреннюю строку, если вы точно не знаете, что вы делаем ...
источник
Учитывая, что они делают разные вещи, вероятно, никогда.
Внутренние строки по соображениям производительности, так что вы можете сравнить их по ссылочному равенству, будут полезны только в том случае, если вы удерживаете ссылки на строки некоторое время - строки, поступающие из пользовательского ввода или ввода-вывода, не будут интернированы.
Это означает, что в вашем приложении вы получаете входные данные из внешнего источника и обрабатываете их в объект, который имеет семантическое значение - скажем, идентификатор - но этот объект имеет тип, неотличимый от необработанных данных, и имеет другие правила относительно того, как программист должен используй это.
Почти всегда лучше создать
UserId
тип, который является интернированным (легко создать универсальный механизм интернирования,java.lang.String
ориентированный на многопотоковое исполнение) и действующий как открытое перечисление, чем перегружать тип ссылочной семантикой, если это происходит с идентификатором пользователя.Таким образом, вы не получите путаницы между тем, была ли интернирована конкретная строка или нет, и вы можете инкапсулировать любое дополнительное поведение, которое вам требуется в открытом перечислении.
источник
Я не знаю о каких-либо преимуществах, и если бы они были, можно было бы подумать, что equals () сама использовала бы intern () внутри (что не так).
Разорение интерна (мифы)
источник
intern
и очень веские причины, которыеequals
по умолчанию этого не делают. Ссылка, которую вы разместили, полная чушь. Последний абзац даже признает, чтоintern
имеет допустимый сценарий использования: обработка тяжелого текста (например, анализатор). Заключение о том, что «[XYZ] опасно, если вы не знаете, что делаете», настолько банально, что это причиняет физический вред.Даниэль Брюкнер абсолютно прав. Интернирование строк предназначено для экономии памяти (кучи). Наша система в настоящее время имеет гигантскую хэш-карту для хранения определенных данных. По мере масштабирования системы хэш-карта будет достаточно большой, чтобы создать кучу памяти (как мы уже тестировали). Благодаря интернированию всех дублированных строк и всех объектов в хэш-карте, это экономит нам значительный объем пространства кучи.
Также в Java 7, интернированные строки больше не живут в PermGen, а вместо этого кучи. Так что вам не нужно беспокоиться о его размере, и да, он получает мусор:
источник
String
экземплярами. Просматривая их содержание, я увидел много дубликатов и решил переключиться на нихintern()
, что сэкономило сотни МБ.Я не знаю об уровне JIT, но есть прямая поддержка байт-кода для пула строк , которая волшебным образом и эффективно реализуется с помощью выделенной
CONSTANT_String_info
структуры (в отличие от большинства других объектов, которые имеют более общие представления).JVMs
JVMS 7 5.1 говорит :
Bytecode
Также полезно взглянуть на реализацию байт-кода в OpenJDK 7.
Если мы декомпилируем:
у нас по постоянному пулу:
и
main
:Обратите внимание, как:
0
и3
: то же самоеldc #2
загружается одна константа (литералы)12
: создается новый экземпляр строки (с#2
аргументом as)35
:a
иc
сравниваются как обычные объекты сif_acmpne
Представление константных строк довольно волшебно в байт-коде:
new String
)и приведенная выше цитата JVMS, по-видимому, говорит о том, что всякий раз, когда Utf8, на который указывают, является одним и тем же, тогда загружаются идентичные экземпляры
ldc
.Я сделал аналогичные тесты для полей, и:
static final String s = "abc"
указывает на таблицу констант через атрибут ConstantValueldc
Бонус : сравните это с целочисленным пулом , который не имеет прямой поддержки байт-кода (т.е. не имеет
CONSTANT_String_info
аналогов).источник
Я бы рассмотрел intern и == - сравнение вместо equals только в случае, когда сравнение equals является узким местом в множественных сравнениях строки. Это маловероятно, чтобы помочь с небольшим количеством сравнений, потому что intern () не является бесплатным. После агрессивного интернирования строк вызовы intern () будут становиться все медленнее и медленнее.
источник
Некоторая утечка памяти может возникнуть в результате использования,
subString()
когда результат невелик по сравнению с исходной строкой, а объект имеет длительный срок службы.Нормальным решением является использование,
new String( s.subString(...))
но когда у вас есть класс, который хранит результат потенциального / вероятногоsubString(...)
и не имеет контроля над вызывающей стороной, вы можете рассмотреть возможность сохраненияintern()
аргументов String, переданных конструктору. Это освобождает потенциальный большой буфер.источник
Строковое интернирование полезно в случае, когда
equals()
метод вызывается часто, потому чтоequals()
метод быстро проверяет, совпадают ли объекты в начале метода.Обычно это происходит при поиске в
Collection
другом коде, но может также проверяться равенство строк.За интернирование приходится платить, но я выполнил микробенчмаркинг некоторого кода и обнаружил, что процесс интернирования увеличивает время выполнения в 10 раз.
Лучшее место для прохождения интернирования - обычно, когда вы читаете ключи, которые хранятся вне кода, поскольку строки в коде автоматически интернируются. Обычно это происходит на этапах инициализации вашего приложения, чтобы предотвратить наказание первого пользователя.
Другое место, где это можно сделать, - при обработке пользовательского ввода, который можно использовать для поиска ключей. Обычно это происходит в вашем обработчике запросов, обратите внимание, что интернированные строки должны быть переданы вниз.
Кроме того, нет смысла проводить интернирование в остальной части кода, поскольку это, как правило, не даст никакой выгоды.
источник
Я бы проголосовал за то, чтобы он не стоил хлопот по обслуживанию.
В большинстве случаев в этом не будет необходимости и не будет никакого выигрыша в производительности, если только ваш код не выполняет большую работу с подстроками. В этом случае класс String будет использовать исходную строку плюс смещение для экономии памяти. Если ваш код часто использует подстроки, то я подозреваю, что это приведет к взрыву требований к памяти.
источник
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»:
Но если вы измените
i
кi.toString()
во второй строке, конечно , этоtrue
.Места, где вы можете надеяться на выгоду от стажировки, включают
Set
иMap
, очевидно. Я надеюсь, что в интернированных строках кэшируются их хэш-коды ... Я думаю, что это будет требованием. И я надеюсь, что я не просто выдал идею, которая могла бы заработать мне миллион долларов. :-)Что касается памяти, также очевидно, что это важный предел, если у вас большой объем Strings или если вы хотите, чтобы объем памяти, используемой вашим программным кодом, был очень маленьким. Если ваш объем -distinct-Strings очень велик, возможно, пришло время рассмотреть возможность использования выделенного программного кода базы данных для управления ими и отдельного сервера базы данных. Аналогично, если вы можете улучшить небольшую программу (которая должна запускаться в 10000 экземпляров одновременно), если она вообще не хранит свои строки.
Создавать новую строку, а затем сразу же отбрасывать ее для
intern()
замены, кажется бесполезной , но не существует четкой альтернативы, кроме как сохранить дублирующую строку. Таким образом, на самом деле стоимость выполнения заключается в поиске вашей строки в пуле интернов, а затем в том, чтобы сборщик мусора мог удалить оригинал. И если это строковый литерал, то он все равно интернируется.Мне интересно,
intern()
может ли злонамеренный программный код использоваться для обнаружения того, что некоторые строки и их ссылки на объекты уже существуют вintern()
пуле и, следовательно, существуют в другом месте сеанса Java, когда это не должно быть известно. Но это возможно только тогда, когда программный код уже используется доверительно, я думаю. Тем не менее, стоит подумать о сторонних библиотеках, которые вы включаете в свою программу для хранения и запоминания своих ПИН-кодов банкоматов!источник
Настоящая причина использовать интерна не выше. Вы можете использовать его после того, как вы получите ошибку нехватки памяти. Многие строки в типичной программе - это String.substring () другой большой строки [подумайте о том, чтобы извлечь имя пользователя из XML-файла размером 100 КБ. Реализация Java заключается в том, что подстрока содержит ссылку на исходную строку и начало + конец в этой огромной строке. (За этим стоит повторное использование одной и той же большой строки)
После 1000 больших файлов, из которых вы сохраняете только 1000 коротких имен, вы сохраняете в памяти целые 1000 файлов! Решение: в этом случае просто используйте smallsubstring.intern ()
источник
Я использую intern для экономии памяти, я храню большой объем данных String в памяти и перехожу к использованию intern (), который сэкономил огромный объем памяти. К сожалению, хотя он использует намного меньше памяти, память, которую он использует, хранится в памяти PermGen, а не в куче, и клиентам сложно объяснить, как увеличить выделение этого типа памяти.
Итак, есть ли альтернатива intern () для уменьшения потребления памяти (преимущества == по сравнению с равными для меня не проблема)
источник
Давайте посмотрим правде в глаза: основной сценарий использования - это когда вы читаете поток данных (либо через входной поток, либо из JDBC ResultSet), и существует множество маленьких строк, которые повторяются повсюду.
Вот небольшой трюк, который дает вам некоторый контроль над тем, какой механизм вы хотели бы использовать для интернализации строк и других неизменяемых, и пример реализации:
Я часто использую это, когда читаю поля из потоков или из ResultSets. Примечание:
LRUCache
это простой кеш на основеLinkedHashMap<K,V>
. Он автоматически вызывает предоставленный пользователемretrieve()
метод для всех пропусков кэша.Способ использовать это - создать его
LRUInternalizer
перед чтением (или чтением), использовать его для интернализации строк и других небольших неизменяемых объектов, а затем освободить его. Например:источник
Я использую его для кэширования содержимого примерно 36000 кодов, которые ссылаются на связанные имена. Я интернирую строки в кеше, потому что многие коды указывают на одну и ту же строку.
Интернируя строки в моем кэше, я гарантирую, что коды, которые указывают на одну и ту же строку, на самом деле указывают на одну и ту же память, тем самым экономя мое место в оперативной памяти.
Если бы интернированные строки были фактически собраны мусором, это не сработало бы для меня вообще. Это в основном сводит на нет цель интернирования. У меня не будет мусора, потому что я держу ссылку на каждую строку в кэше.
источник
Стоимость интернирования строки намного больше, чем время, сэкономленное при сравнении одной строки A.equals (B). Используйте его (из соображений производительности) только в том случае, если вы постоянно используете одни и те же неизменные строковые переменные. Например, если вы регулярно перебираете стабильный список строк для обновления некоторых карт, привязанных к одному и тому же строковому полю, вы можете получить хорошее сохранение.
Я бы предложил использовать интернирование строк для настройки производительности, когда вы оптимизируете определенные части своего кода.
Также помните, что String неизменны и не делают глупую ошибку
не забудьте сделать
источник
Если вы ищете неограниченную замену для String.intern, а также для сборки мусора, следующее хорошо работает для меня.
Конечно, если вы можете приблизительно оценить, сколько будет различных строк, просто используйте String.intern () с -XX: StringTableSize = highEnoughValue .
источник