Хорошо ли использовать java.lang.String.intern ()?

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

  • Когда я буду использовать эту функцию в пользу String.equals() ?
  • Существуют ли побочные эффекты, не упомянутые в Javadoc, т.е. более или менее оптимизация JIT-компилятором?
  • Существуют ли дополнительные возможности String.intern() ?

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

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

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

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

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

(от JGuru)

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

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

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

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 () .

Недавно я написал статью о реализации 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 (по умолчанию, вероятно, слишком мало, установите Prime-номер)

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

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

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

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

Строка String.intern () – garbage collection

В новых версиях JVM также интернализованная строка – это garbage collection, если какой-либо объект не ссылается на него.

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

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

Преодоление мифов ()

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

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

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

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

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

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

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

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

JVMs

JVMS 7 5.1 говорит :

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

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

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

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

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

  • В противном случае создается новый экземпляр classа String, содержащий последовательность кодовых точек Unicode, заданную структурой CONSTANT_String_info; ссылка на этот экземпляр classа является результатом строкового литерала. Наконец, вызывается метод 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."":(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 и та же ldc #2 (литералы)
  • 12 : создается новый экземпляр строки (с аргументом #2 качестве аргумента)
  • 35 : a и c сравниваются как обычные объекты с if_acmpne

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

В большинстве случаев не будет необходимости, и никакой выгоды от производительности, если вы не используете код, очень много работает с подстроками. В этом случае class 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() ли String.equals() что вы передали ему String, а не какой-либо другой объект, перед использованием "==" ? Я не имею права говорить, но я бы не догадался, потому что в большинстве equals() такие операции equals() будут String для String, поэтому тест почти всегда передается. Действительно, приоритет «==» внутри String.equals() подразумевает уверенность в том, что вы часто сравниваете String с одним и тем же фактическим объектом.

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

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

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

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

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

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

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

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

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

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

Итак, есть ли альтернатива intern () для сокращения потребления памяти, (выгоды от сравнения с равными значениями равны для меня не для меня)

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

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

 /** * Extends the notion of String.intern() to different mechanisms and * different types. For example, an implementation can use an * LRUCache, or a WeakHashMap. */ public interface Internalizer { public T get(T obj); } public static class LRUInternalizer implements Internalizer { private final LRUCache cache; public LRUInternalizer(int size) { cache = new LRUCache(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 { @Override public String get(String obj) { return obj.intern(); } } 

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

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

 Internalizer internalizer = new LRUInternalizer(2048); // ... get some object "input" that stream fields for (String s : input.nextField()) { s = internalizer.get(s); // store s... } 

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

Проводя строки в кеше, я гарантирую, что коды, указывающие на одну и ту же строку, фактически указывают на одну и ту же память, тем самым сохраняя пространство памяти.

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

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

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

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

 String a = SOME_RANDOM_VALUE a.intern() 

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

 String a = SOME_RANDOM_VALUE.intern() 

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

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

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

  • Простой способ подсчета символов в строке
  • динамическая строка с использованием String.xml?
  • как проверить начало строки в C ++
  • Строка соединения SQL Server Express для Entity Framework Code First
  • Сравнение строк, нечувствительных к регистру, в C ++
  • Удаление дубликатов из строки в Java
  • Как удалить все неабелевые числовые символы из строки в MySQL?
  • Используйте Java и RegEx для преобразования оболочки в строку
  • байты строки в java?
  • Получение и удаление первого символа строки
  • Как настроить ProviderManifestToken для EF Code First
  • Interesting Posts

    Как показать / скрыть элемент на флажке проверенных / непроверенных состояний с помощью jQuery?

    CSS “и” and “или”

    Запуск MSBuild не читается SDKToolsPath

    ERROR 2003 (HY000): невозможно подключиться к серверу MySQL на «127.0.0.1» (111)

    Непосредственное создание фиктивной переменной, заданной в разреженной матрице в R

    Получение промежутка времени между двумя временами в C #?

    Apache без подключения к Интернету

    Можно ли использовать другую подпись в Outlook для внутренней или внешней почты?

    Я хотел бы изменить, как клавиша FN влияет на функциональные клавиши на Lenovo IdeaPad z500

    Ruby on Rails 3, несовместимые кодировки символов: UTF-8 и ASCII-8BIT с i18n

    Разделение кадра данных переменной

    Проводник Очень медленный и нереактивный

    Вставка кода с помощью XJC + xsd + jxb с использованием опций «-Xinject-code -extension»

    Есть ли какие-либо макрокоманды с клавиатурной мышью, чтобы сэкономить время на любой повторной задаче?

    Использование WebClient в C # есть способ получить URL-адрес сайта после перенаправления?

    Давайте будем гением компьютера.