O que é o Java String interning?

o que é String Interning {[[3]} em Java, quando devo usá-lo, e porquê?

5 answers

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

Basicamente a fazer cordas.intern () em uma série de strings irá garantir que todas as strings com o mesmo conteúdo compartilhem a mesma memória. Então, se você tem uma lista de nomes onde 'john' aparece 1000 vezes, ao internar você garante que apenas um 'john' é realmente alocado memória. Isto pode ser útil para reduzir as necessidades de memória do seu programa. Mas esteja ciente de que o cache é mantido pela JVM em memória permanente pool que é geralmente limitado em tamanho em comparação com heap para que você não deve usar o intern se você não tem muitos valores duplicados.

Mais sobre as restrições de memória de usar o intern ()

Por um lado, é verdade que você pode remover duplicados de texto por a internalizá-los. O problema é que os strings internalizados vão para a geração permanente, que é uma área da JVM que está reservada para objectos não utilizáveis, como Classes, métodos e outros JVM interno objecto. O tamanho desta área é limitado, e é geralmente muito menor do que o Monte. Chamar o interno () numa String tem o efeito de se mover a partir do Monte para a geração permanente, e você arrisca estamos a ficar sem espaço de PermGen.

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


De JDK 7( quero dizer em HotSpot), algo mudou.

No JDK 7, as cadeias de caracteres são não mais alocados na geração permanente do Java heap, mas são alocados na parte principal do Java heap (conhecido como as gerações jovens e velhas), juntamente com os outros objetos criados pela aplicação. Esta mudança resultará em mais dados residindo no heap Java principal, e menos dados na geração permanente, e assim pode exigir tamanhos de heap para serem ajustados. A maioria das aplicações verá apenas diferenças relativamente pequenas no uso de heap devido a esta mudança, mas maiores aplicações que carregam muitas classes ou fazem uso pesado da String.o método interno() verá diferenças mais significativas.

-- From Java SE 7 Features and Enhancements

Update: as strings Interned são armazenadas no heap principal a partir do Java 7 em diante. http://www.oracle.com/technetwork/java/javase/jdk7-relnotes-418459.html#jdk7changes

 181
Author: Ashwinee K Jha, 2014-01-27 06:55:57
Há algumas perguntas de "entrevista cativante" por que você tem
String s1 = "testString";
String s2 = "testString";
if(s1 == s2)System.out.println("equals!");

Se comparar as cadeias de caracteres, deve utilizar equals(). A impressão acima será igual, porque o testString está pronto fechado para si pelo compilador. Você pode internar as cadeias de caracteres você mesmo usando o método interno como é mostrado em respostas anteriores....

 53
Author: maslan, 2016-09-01 12:54:29

JLS

O JLS 7 3.10.5 define-o e dá um exemplo prático:

Além disso, um texto literal sempre se refere à mesma instância da cadeia de classes. Isto é porque literais de string - ou, mais geralmente, strings que são os valores de expressões constantes (§15.28) - são "internados" de modo a compartilhar instâncias únicas, usando a String de método.Estagiario.

Exemplo 3.10.5-1. Literais De Textos

O programa que consiste da unidade de compilação(§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"; }

E a unidade de compilação:

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

Produz a produção:

true true true true false true

JVMS

JVMS 7 5.1 diz diz que internar é implementado magicamente e eficiente, com um dedicado CONSTANT_String_info struct (ao contrário da maioria dos outros objetos que tenham mais genérico representações):

Um texto literal é uma referência a uma instância de cadeia de classes, e é derivado de uma Constante_ String_info estrutura (§4.4.3) na representação binária de uma classe ou interface. A estrutura CONSTANT_String_info dá a sequência de pontos de código Unicode que constituem a cadeia literal.

A linguagem de programação Java requer que literais de string idênticos (isto é, literais que contêm a mesma sequência de pontos de código) devem se referir à mesma instância de string de classe (JLS §3.10.5). Além disso, se o método String.o interno é chamado em qualquer cadeia, o resultado é uma referência ao a mesma instância de classe que seria devolvida se essa corda aparecesse como um literal. Assim, a seguinte expressão deve ter o valor verdadeiro:

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

Para derivar um texto literal, a máquina virtual Java examina a sequência de pontos de código dada pela estrutura CONSTANT_String_info.

  • Se o método String.o intern já foi chamado em uma instância de cadeia de classe contendo uma sequência de pontos de código Unicode idênticos aos dados pelo CONSTANT_String_info estrutura, então o resultado da derivação literal de cadeia é uma referência a essa mesma instância de cadeia de classe.

  • Caso contrário, uma nova instância de cadeia de classe é criada contendo a sequência de pontos de código Unicode dada pela estrutura CONSTANT_String_info; uma referência a essa instância de classe é o resultado de derivação literal de cadeia. Finalmente, o método interno da nova instância String é invocado.

Bytecode

Vamos descompilar um código OpenJDK 7 bytecode para ver o interning em acção.

Se descompilar:

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);
    }
}

Temos uma piscina constante:

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

E 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

Nota:

  • 0 e 3: a mesma constante ldc #2 é carregada (os literais)
  • 12: uma nova instância de cadeia de caracteres é criada (com #2 como argumento)
  • 35: a e c são comparados como objectos normais com if_acmpne

A representação de cordas constantes é bastante mágica no bytecode:

  • ele tem uma estrutura específica CONSTANT_String_info , ao contrário de objetos regulares (e.g. new String)
  • a estrutura aponta para uma estrutura CONSTANT_Utf8_info que contém os dados. Esses são os únicos dados necessários para representar a string.

E a citação do JVMS acima parece dizer que sempre que o Utf8 apontado é o mesmo, então idêntico as instâncias são carregadas por ldc.

Fiz testes semelhantes para campos, e:

  • static final String s = "abc" aponta para a tabela constante através do atributo ConstantValue
  • os campos não-finais não têm esse atributo, mas ainda podem ser inicializados com ldc

Conclusão : existe suporte directo de bytecode para o conjunto de cadeias de caracteres, e a representação da memória é eficiente.

Bónus: comparar com o inteiro pool , que não tem suporte bytecode directo (ou seja, nenhum análogo CONSTANT_String_info).

Actualizar para Java 8 ou plus . Em Java 8, o espaço PermGen (geração permanente) é removido e substituído pelo espaço Meta. A memória String pool é movida para o monte de JVM.

Comparado com Java 7, O tamanho da String pool é aumentado no heap. Portanto, você tem mais espaço para Strings internalizadas, mas você tem menos memória para toda a aplicação.

Mais uma coisa, você já sabia que ao comparar 2 (referências de) objectos em Java, ' ==' é usado para comparar a referência do objeto, 'equals ' é usado para comparar o conteúdo do objeto.

Vamos verificar este código.
String value1 = "70";
String value2 = "70";
String value3 = new Integer(70).toString();

Resultado:

value1 == value2 ---> verdadeiro

value1 == value3 ---> falso

value1.equals(value3) ---> verdadeiro

value1 == value3.intern() ---> verdadeiro

É por isso que deve usar 'equals' para comparar 2 objectos de texto. E é assim que intern() é útil.

 2
Author: nguyentt, 2018-08-07 20:39:55

String interning é uma técnica de otimização pelo compilador. Se você tem dois literais de string idênticos em uma unidade de compilação, então o código gerado garante que existe apenas um objeto de string criado para toda a instância desse literal(caracteres incluídos em aspas duplas) dentro do conjunto.

Sou de C# background, por isso posso explicar dando um exemplo disso:

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

Resultado das seguintes comparações:

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

Note1 : Objectos são comparados por referência.

Note2 : Typ of (int). o nome é avaliado pelo método de reflexão, pelo que não é avaliado na altura de compilação. Aqui estas comparações são feitas no tempo de compilação.

Análise dos Resultados: 1) verdadeiro porque ambos contêm o mesmo literal e assim o código gerado terá apenas um objeto referenciando "Int32". Ver Nota 1 .

2) verdadeiro porque o conteúdo de ambos o valor é verificado que é mesmo.

3) falso porque str2 e obj não têm o mesmo literal. Ver Nota 2 .

 0
Author: Robin Gupta, 2017-09-24 04:51:15