Вопрос по string, java – Что такое интернирование Java String?

189

Что такоеString Interning в Java, когда я должен его использовать, иwhy?

Есть лиString.intern() зависит отClassLoaderозначает, что разные загрузчики классов создают "разные"Stringс, в результате чего разныеinterns? AlikElzin-kilaka
увидетьWhen should we use intern method of String? zeller
Пример проверки строки проверки:algs4.cs.princeton.edu/12oop/MutableString.java.html Ronak Poriya
еслиString a = new String("abc"); String b = new String("abc");  затемa.intern() == b.intern() Asanka Siriwardena

Ваш Ответ

5   ответов
6

Update for Java 8 or plus. PermGen (Permanent Generation) space is removed and replaced by Meta Space. The String pool memory is moved to the heap of JVM.

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

Еще одна вещь, вы уже знаете, что при сравнении 2 (ссылок) объектов в Java & apos;==& APOS; используется для сравнения ссылки на объект, & apos;equals& APOS; используется для сравнения содержимого объекта.

Давайте проверим этот код:

String value1 = "70";
String value2 = "70";
String value3 = new Integer(70).toString();

Результат:

value1 == value2          --- & GT; правда

value1 == value3          --- & GT; ложный

value1.equals(value3)     --- & GT; правда

value1 == value3.intern() --- & GT; правда

Вот почему вы должны использовать & apos;equals& APOS; сравнить 2 объекта String. И это какintern() Полезно.

56

String s1 = "testString";
String s2 = "testString";
if(s1 == s2)System.out.println("equals!");

Если вы должны сравнить строки, вы должны использоватьequals(), Выше будет печатать равно, потому чтоtestString уже готовinterned для вас компилятором. Вы можете интернировать строки самостоятельно, используя метод intern, как показано в предыдущих ответах ....

2

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

Я из C # фона, поэтому я могу объяснить, приведя пример из этого:

object obj = "Int32";
string str1 = "Int32";
string str2 = typeof(int).Name;

вывод следующих сравнений:

Console.WriteLine(obj == str1); // true
Console.WriteLine(str1 == str2); // true    
Console.WriteLine(obj == str2); // false !?

Note1: Объекты сравниваются по ссылке.

Note2: typeof (int). Имя оценивается методом отражения, поэтому оно не оценивается во время компиляции.Here these comparisons are made at compile time.

Analysis of the Results: 1) истина, поскольку они оба содержат один и тот же литерал, и поэтому сгенерированный код будет иметь только один объект, ссылающийся на "Int32".See Note 1.

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

3) ЛОЖЬ, потому что str2 и obj не имеют одинаковые литералы. УвидетьNote 2.

Это сильнее, чем это. Любой строковый литерал, загруженный одним и тем же загрузчиком классов, будет ссылаться на одну и ту же строку. Смотрите спецификации JLS и JVM.
195

http://docs.oracle.com/javase/7/docs/api/java/lang/String.html#intern ()

По сути, выполнение String.intern () для ряда строк гарантирует, что все строки, имеющие одинаковое содержимое, совместно используют одну и ту же память. Так что, если у вас есть список имен, где «Джон» появляется 1000 раз, проходя стажировку, вы гарантируете, что только один «Джон» фактически выделена память.

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

Подробнее об ограничениях памяти при использовании intern ()

On one hand, it is true that you can remove String duplicates by internalizing them. The problem is that the internalized strings go to the Permanent Generation, which is an area of the JVM that is reserved for non-user objects, like Classes, Methods and other internal JVM objects. The size of this area is limited, and is usually much smaller than the heap. Calling intern() on a String has the effect of moving it out from the heap into the permanent generation, and you risk running out of PermGen space.

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

С JDK 7 (я имею в виду в HotSpot) что-то изменилось.

In JDK 7, interned strings are no longer allocated in the permanent generation of the Java heap, but are instead allocated in the main part of the Java heap (known as the young and old generations), along with the other objects created by the application. This change will result in more data residing in the main Java heap, and less data in the permanent generation, and thus may require heap sizes to be adjusted. Most applications will see only relatively small differences in heap usage due to this change, but larger applications that load many classes or make heavy use of the String.intern() method will see more significant differences.

-- ОтJava SE 7 Особенности и улучшения

Обновление: Interned строки хранятся в основной куче начиная с Java 7 и далее.http://www.oracle.com/technetwork/java/javase/jdk7-relnotes-418459.html#jdk7changes

Этот ответ должен быть обновлен для Java 8
"But be aware that the cache is maintained by JVM in permanent memory pool which is usually limited in size ......" Вы можете это объяснить? Я не понял saplingPro
@grassPro: Да, это своего рода кеширование, изначально предоставляемое JVM. Как примечание, из-за слияния Sun / Oracle JVM и JRockit инженеры JVM пытаются избавиться от области постоянной памяти в JDK 8 (openjdk.java.net/jeps/122), поэтому в будущем не будет каких-либо ограничений по размеру.
"интернированный" строки хранятся в специальной области памяти в JVM. Эта область памяти обычно имеет фиксированный размер и не является частью обычной кучи Java, где хранятся другие данные. Из-за фиксированного размера может случиться так, что эта область постоянной памяти заполняется всеми вашими строками, что приводит к ужасным проблемам (классы не могут быть загружены и другие вещи).
Программисты также должны знать, что интернирование строк может иметь последствия для безопасности. Если у вас есть чувствительный текст, такой как пароли, в виде строк в памяти, он может оставаться в памяти в течение очень долгого времени, даже если фактические строковые объекты уже давно были GC-d. Это может быть неприятно, если плохие парни каким-то образом получат доступ к дампу памяти. Эта проблема существует даже без интернирования (поскольку GC является недетерминированной для начала и т. Д.), Но делает ее несколько хуже. Это всегда хорошая идея для использованияchar[] вместоString для чувствительного текста и обнулите его, как только он больше не нужен.
34
JLS

JLS 7 3.10.5 определяет его и дает практический пример:

Moreover, a string literal always refers to the same instance of class String. This is because string literals - or, more generally, strings that are the values of constant expressions (§15.28) - are "interned" so as to share unique instances, using the method String.intern.

Example 3.10.5-1. String Literals

The program consisting of the compilation unit (§7.3):

package testPackage;
class Test {
    public static void main(String[] args) {
        String hello = "Hello", lo = "lo";
        System.out.print((hello == "Hello") + " ");
        System.out.print((Other.hello == hello) + " ");
        System.out.print((other.Other.hello == hello) + " ");
        System.out.print((hello == ("Hel"+"lo")) + " ");
        System.out.print((hello == ("Hel"+lo)) + " ");
        System.out.println(hello == ("Hel"+lo).intern());
    }
}
class Other { static String hello = "Hello"; }

and the compilation unit:

package other;
public class Other { public static String hello = "Hello"; }

produces the output:

true true true true false true
JVMS

JVMS 7 5.1 говорит говорит, что стажировка осуществляется волшебно и эффективно с выделеннымCONSTANT_String_info struct (в отличие от большинства других объектов, которые имеют более общие представления):

A string literal is a reference to an instance of class String, and is derived from a CONSTANT_String_info structure (§4.4.3) in the binary representation of a class or interface. The CONSTANT_String_info structure gives the sequence of Unicode code points constituting the string literal.

The Java programming language requires that identical string literals (that is, literals that contain the same sequence of code points) must refer to the same instance of class String (JLS §3.10.5). In addition, if the method String.intern is called on any string, the result is a reference to the same class instance that would be returned if that string appeared as a literal. Thus, the following expression must have the value true:

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

To derive a string literal, the Java Virtual Machine examines the sequence of code points given by the CONSTANT_String_info structure.

If the method String.intern has previously been called on an instance of class String containing a sequence of Unicode code points identical to that given by the CONSTANT_String_info structure, then the result of string literal derivation is a reference to that same instance of class String.

Otherwise, a new instance of class String is created containing the sequence of Unicode code points given by the CONSTANT_String_info structure; a reference to that class instance is the result of string literal derivation. Finally, the intern method of the new String instance is invoked.

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 and 3: the same ldc #2 constant is loaded (the literals) 12: a new string instance is created (with #2 as argument) 35: a and c are compared as regular objects with if_acmpne

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

it has a dedicated CONSTANT_String_info structure, unlike regular objects (e.g. new String) the struct points to a CONSTANT_Utf8_info Structure that contains the data. That is the only necessary data to represent the string.

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

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

static final String s = "abc" points to the constant table through the ConstantValue Attribute non-final fields don't have that attribute, but can still be initialized with ldc

Conclusion: есть прямая поддержка байт-кода для пула строк, и представление памяти эффективно.

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

Похожие вопросы