É uma boa prática usar java.idioma.Cadeia.interno?

o Javadoc sobre String.intern() não dá muitos detalhes. (Em poucas palavras: ele retorna uma representação canônica da string, permitindo que strings internadas sejam comparadas usando ==)

  • quando usaria esta função a favor de String.equals()?
  • Existem efeitos colaterais não mencionados no Javadoc, ou seja, mais ou menos otimização pelo compilador JIT?
  • existem outras utilizações de String.intern()?
 189
Author: Daniel Rikowski, 2009-07-07

20 answers

Quando usaria esta função a favor de String.igual a ()

Quando Precisa de velocidade uma vez que pode comparar cadeias de caracteres por referência (==é mais rápido que igual)

Existem efeitos secundários não mencionados no Javadoc?

A principal desvantagem é que você tem que se lembrar de se certificar de que você realmente fazer interno() todas as cordas que você vai comparar. É fácil esquecer de internar () todas as cordas e, em seguida, você pode obter resultados confusamente incorrectos. Além disso, para o bem de todos, por favor, certifique-se de documentar muito claramente que está a confiar nas cordas a serem internalizadas.

A segunda desvantagem Se você decidir internalizar strings é que o método interno() é relativamente caro. Ele tem que gerenciar o conjunto de strings únicos para que ele faça um pouco de trabalho (mesmo que a string já foi internalizada). Por isso, tenha cuidado no seu design de código para que você, por exemplo, internar () todas as cordas apropriadas na entrada não precisas de te preocupar mais com isso.

(de JGuru)

Terceira desvantagem( Java 7 ou menos apenas): as cadeias interned vivem no espaço PermGen, que normalmente é bastante pequeno; você pode correr para um OutOfMemoryError com bastante espaço livre de heap.

(de Michael Borgwardt)

 121
Author: dfa, 2015-01-02 09:05:24

Isto não tem (quase) nada a ver com a comparação de cadeias. O string interning destina-se a guardar a memória se tiver muitas cadeias de caracteres com o mesmo conteúdo na sua aplicação. Ao usar {[[0]} a aplicação só terá uma instância a longo prazo e um efeito colateral é que você pode realizar uma comparação rápida de igualdade de referência em vez de uma comparação normal de string (mas isso geralmente não é aconselhável, porque é realmente fácil de quebrar, esquecendo-se de internar apenas um único instancia).

 188
Author: Daniel Brückner, 2009-07-07 08:45:05

String.intern() é definitivamente lixo coletado em JVMs modernos.
O seguinte nunca fica sem memória, por causa da atividade 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();
        }
    }
}

Veja mais (de mim) sobre o mito de cordas não geladas.interno().

 36
Author: Gili Nachum, 2014-04-28 18:22:01

Escrevi recentemente um artigo sobre String.implementação interna () em Java 6, 7 e 8: fio.interna em Java 6, 7 e 8-string pooling .

Espero que contenha informação suficiente sobre a situação actual com o String pooling em Java.

Em poucas palavras:

  • evite {[[0]} em Java 6, porque vai para PermGen
  • Prefer String.intern() in Java 7 & Java 8: it uses 4-5x less memory than rolling your own object pool
  • Certifica-te que tune -XX:StringTableSize (o valor por omissão é provavelmente demasiado pequeno; Defina um número primo)
 15
Author: mik1, 2013-11-02 16:09:54

Comparar cadeias com = = é muito mais rápido do que com iguais()

5 tempo mais rápido, mas uma vez que a comparação de cadeia geralmente representa apenas uma pequena porcentagem do tempo total de execução de uma aplicação, o ganho global é muito menor do que isso, e o ganho final será diluído a alguns por cento.

String.interna () puxe a corda para longe de Heap e colocá - la em PermGen

String internalizado são colocados em uma área de armazenamento diferente : geração permanente que é uma área da JVM que está reservada para objectos não-utilizadores, como Classes, métodos e outros objectos internos da JVM. O tamanho desta área é limitado e o é muito precioso do que heap. Sendo esta área menor que Heap, há mais probabilidade de usar todo o espaço e obter um OutOfMemoryException.

String.string interna() são lixo coletado

nas novas versões do JVM também internalizado string são coletados lixo quando não é referenciado por nenhum objecto.

Tendo em mente o 3 ponto acima você poderia deduzir que String interna () poderia ser útil apenas em poucas situações quando você faz uma grande comparação de string, no entanto é melhor não usar string interna se você não sabe exatamente o que você está fazendo ...

 13
Author: aleroot, 2011-09-24 10:00:40

Não estou ciente de quaisquer vantagens, e se houvesse em uma pessoa pensaria que igual() seria ele mesmo usar interno() internamente (o que não faz).

Busting intern () myths

 8
Author: objects, 2009-07-07 08:41:55

Quando usaria esta função a favor de String.igual a ()

Dado que fazem coisas diferentes, provavelmente nunca.

A utilização de strings por razões de desempenho, para que possa compará - las para a igualdade de referência, só será benéfica se tiver referências às strings durante algum tempo-strings provenientes da entrada do utilizador ou se as IO não forem internadas.

Isso significa que na sua aplicação recebe dados de uma fonte externa e processa-os em um objeto que tem um valor semântico - um identificador diz - mas esse objeto tem um tipo indistinguível dos dados raw, e tem regras diferentes sobre como o programador deve usá-lo.

É quase sempre melhor criar um tipo {[[0]} que é internado ( é fácil criar um mecanismo de interning genérico de segurança de thread ) e age como um enum aberto, do que sobrecarregar o tipo java.lang.String com semântica de referência, se por acaso for um ID de utilizador.

Assim não ficas confuso. entre se uma determinada String foi ou não internada, e você pode encapsular qualquer comportamento adicional que você precisa no enum aberto.
 6
Author: Pete Kirkham, 2009-07-07 08:58:18

Existem efeitos colaterais não mencionados no Javadoc, ou seja, mais ou menos otimização pelo compilador JIT?

Eu não sei sobre o nível JIT, mas existe suporte directo de bytecode para o conjunto de caracteres, que é implementado magicamente e eficientemente com uma estrutura CONSTANT_String_info dedicada (ao contrário da maioria dos outros objectos que têm representações mais genéricas).

JVMS

JVMS 7 5.1 diz:

Um texto literal é uma referência a uma instância de cadeia de classe, e é derivada de uma estrutura CONSTANT_String_info (§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). Em adição, se o método String.o interno é chamado em qualquer cadeia, o resultado é uma referência à mesma instância de classe que seria retornada se essa cadeia 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.estágio já foi chamado em um 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.

  • 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. Por último, o o método interno da nova instância String é invocado.

Bytecode

Também é instrutivo olhar para a implementação bytecode no OpenJDK 7.

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 ldc #2 constante está carregada (os literais)
  • 12: uma nova instância de texto é criada (com #2 como argumento)
  • 35: a e c são comparados como objectos normais com if_acmpne

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

  • tem uma estrutura específica CONSTANT_String_info , ao contrário dos objectos regulares (ex. new String)
  • a estrutura indica uma estrutura constante que contém os dados. Esses são os únicos dados necessários para representar a string.

E a citação da JVMS acima parece dizer que sempre que o Utf8 apontado é o mesmo, então instâncias idênticas 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

Bônus : compare isso com o conjunto inteiro, que não tem bytecode direto suporte (ou seja, n. o CONSTANT_String_info analógico).

Eu examinaria interno e = = = - comparação em vez de igual só no caso de igual-comparação sendo gargalo em múltiplas comparações de cadeia. Isto é altamente improvável para ajudar com um pequeno número de comparações, porque interno() não é livre. Depois de amarrar agressivamente strings você vai encontrar chamadas para internar () ficando mais lento e mais lento.

 2
Author: Mikko Maunu, 2009-07-07 08:46:27

Um tipo de fuga de memória pode vir do uso de {[[0]} quando o resultado é pequeno em comparação com a cadeia de origem e o objeto tem uma vida longa.

A solução normal é usar new String( s.subString(...)) mas quando você tem uma classe que armazena o resultado de uma possível/provável subString(...) e não tem nenhum controle sobre o chamador, você pode considerar para armazenar o intern() a Seqüência de argumentos passados para o construtor. Isso libera o potencial buffer grande.

 2
Author: eremmel, 2012-07-16 13:42:43
O Daniel Brückner tem toda a razão. String interning destina-se a salvar a memória (heap). o nosso sistema tem actualmente um hashmap gigante para guardar certos dados. Como escalas de sistema, o hashmap será grande o suficiente para fazer o heap fora da memória (Como nós testamos). Ao colocar todas as strings duplicadas todos os objetos no hashmap, ele nos poupa uma quantidade significativa de espaço heap.

Também em Java 7, as cadeias interned não vivem mais em PermGen,mas em heap.Então, tu ... não precisa se preocupar com seu tamanho e sim ele recebe lixo coletado:

No JDK 7, as cadeias de caracteres internadas deixaram de ser atribuídas a título permanente. geração do Java heap, mas em vez disso são alocados na principal parte do Monte Java (conhecido como as gerações jovens e velhas), ao longo com os outros objetos criados pela aplicação. Esta mudança result in more data residing in the main Java heap, and less data in a geração permanente, e assim pode exigir tamanhos de heap ser ajustado. A maioria das aplicações verá apenas diferenças relativamente pequenas no uso de heap devido a esta mudança, mas aplicações maiores que carregam muitas classes ou fazer uso pesado da corda.método interno () vai ver diferenças mais significativas.

 2
Author: xli, 2013-04-26 00:28:10

O interning de cadeias de caracteres é útil no caso em que o método equals() está a ser invocado frequentemente porque o método equals() faz uma verificação rápida para ver se os objectos são os mesmos no início do método.

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

Isto geralmente ocorre ao pesquisar através de um Collection embora outro código também possa fazer verificações de igualdade de string.

No entanto, há um custo envolvido no internamento, eu executei uma microbenchmark de algum código e descobri que o processo de internamento aumenta o tempo de execução por um factor de 10.

O melhor lugar para fazer o interning é normalmente quando você está lendo chaves que são armazenadas fora do código como strings no código são internados automaticamente. Isso normalmente aconteceria nas fases de inicialização de sua aplicação, a fim de evitar a penalidade do primeiro usuário.

Outro lugar onde isso pode ser feito é quando o processamento de entrada do usuário pode ser usado para fazer pesquisas de chaves. Isto normalmente acontece em seu processador de pedido, note que o Inter - as cordas devem ser passadas.

Além disso, não faz muito sentido fazer estágio no resto do código, pois geralmente não dá nenhum benefício.
 2
Author: Archimedes Trajano, 2014-08-03 16:30:34
Eu votaria para que não valesse a pena o incómodo da manutenção. A maior parte do tempo, não haverá necessidade, nem benefício de desempenho, a menos que o código faça muito trabalho com substratos. Nesse caso, a classe String irá usar a string original mais um deslocamento para salvar a memória. Se o seu código usa muitas substrings, então suspeito que só vai causar a explosão das suas necessidades de memória.
 1
Author: wm_eddie, 2009-07-07 09:27:54

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

Afirma que String.equals() usa "==" para comparar String objectos antes, de acordo com

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

Compara os comprimentos das cordas e o conteúdo. (A propósito, as cadeias de códigos de produtos num catálogo de vendas podem ter o mesmo comprimento - o BIC0417 é um o capacete de segurança do bicicista, TIG0003 é um tigre macho vivo. - provavelmente precisas de todo o tipo de licenças para encomendar uma dessas. E talvez seja melhor pedires um capacete de segurança ao mesmo tempo.)

Então soa como se você tivesse um benefício de substituir suas cadeias de caracteres por sua versão intern(), mas você tem segurança - e legibilidade e conformidade padrão - - - sem-usar "==" para equals() em sua programação. E a maior parte do que vou dizer depende de isso ser verdade, se for verdadeiro.

Mas será que String.equals() Testa que passaste por ela uma cadeia e não outro objecto, antes de usar "=="? Eu não sou qualificado para dizer, mas eu acho que não, porque esmagadoramente a maioria dessas operações equals() será String to String, de modo que o teste é quase sempre passado. De fato, priorizar " = " dentro String.equals() implica uma confiança de que você frequentemente está comparando a cadeia com o mesmo objeto real.

Espero que ninguém se surpreenda que as seguintes linhas produzam um resultado de "falso":
    Integer i = 1;
    System.out.println("1".equals(i));
Mas se você mudar i para i.toString() na segunda linha, é claro que é true.

Locais onde você pode esperar um benefício do internamento incluem Set e Map, obviamente. Espero que as cordas fechadas tenham os hashcodes em cache... Penso que isso seria um requisito. E espero não ter dado uma ideia que me possa ganhar um milhão de dólares. :-)

Quanto à memória, também é óbvio que esse é um limite importante se o seu volume de Strings é grande, ou se você quer que a memória usada pelo seu código de programa seja muito pequena. Se o seu volume de Strings distintas é muito grande, então pode ser hora de considerar usar o código de programa de banco de dados Dedicado para geri-los, e um servidor de banco de dados separado. Da mesma forma, se você pode melhorar um pequeno programa (que precisa executar em 10000 instâncias simultaneamente) por não ter que armazenar suas cordas em tudo. É um desperdício criar uma nova corda e descartá-la imediatamente. para seu substituto intern(), mas não há uma alternativa clara, exceto para manter a cadeia duplicada. Então realmente o custo de execução é de procurar o seu fio na piscina interna e, em seguida, permitir que o coletor de lixo para se livrar do original. E se é literal, então já vem internado de qualquer maneira.

Estou a perguntar-me se intern() pode ser abusado por um código de programa malicioso para detectar se alguma cadeia de caracteres e as suas referências de objectos já existem no conjunto intern() , e, portanto, existem em outros lugares na sessão Java, quando isso não deveria ser conhecido. Mas isso só seria possível quando o código do programa já está sendo usado de forma confiável, eu acho. Ainda assim, é algo a considerar sobre as bibliotecas de terceiros que você inclui em seu programa para armazenar e lembrar seus números PIN ATM!

 1
Author: Robert Carnegie, 2012-10-27 07:14:32

A verdadeira razão para usar o estagiário não é a acima. Podes usá-lo depois de saíres da memória. Muitas das strings em um programa típico são String.substring () of other big string [think of taking out a user-name from a 100K xml file. A implementação java é que , a substring contém uma referência à string original e o start+end nessa string enorme. (O pensamento por trás é uma reutilização da mesma cadeia grande)

Depois de 1000 ficheiros grandes, dos quais só gravas 1000 nomes curtos, você vai manter na memória todos os 1000 arquivos! Solução: neste cenário, basta usar pequenas suturas.intern ()

 0
Author: asaf, 2010-07-22 20:25:04

Estou a usar o intern para salvar a memória, tenho uma grande quantidade de dados String na memória e a mover-me para usar o intern() salvou uma enorme quantidade de memória. Infelizmente, embora use muito menos memória a memória que ele usa é armazenado na memória PermGen não Heap e é difícil de explicar aos clientes como aumentar a alocação deste tipo de memória.

Existe uma alternativa ao intern() para reduzir o consumo de memória, (O = = versus iguais benefícios de desempenho não é um aissue for me)

 0
Author: Paul Taylor, 2010-09-09 13:27:06

Vamos encarar isto: o principal cenário de uso é quando você lê um fluxo de dados (seja através de um fluxo de entrada, ou de um conjunto de resultados JDBC) e há uma miríade de pequenas cadeias que são repetidas Todas ao longo.

Aqui está um pequeno truque que lhe dá algum controle sobre que tipo de mecanismo você gostaria de usar para internalizar Strings e outros imutáveis, e um exemplo de implementação:
/**
 * 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();
    }
}
Uso isso muitas vezes quando leio campos de correntes ou de resultados. Notar: LRUCache é um cache simples baseado em LinkedHashMap<K,V>. Ele automaticamente chama o método fornecido pelo usuário retrieve() para todas as falhas de cache.

A forma de usar isto é criar um LRUInternalizer antes de ler (ou ler), usá-lo para interiorizar Cadeias de caracteres e outros pequenos objectos imutáveis, e depois libertá-lo. Por exemplo:

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
Author: Pierre D, 2012-08-14 21:53:58

Estou a usá-lo para guardar o conteúdo de aproximadamente 36000 códigos que ligam a nomes associados. Eu interesso as cordas no cache porque muitos dos códigos apontam para a mesma corda.

Ao colocar as cordas no meu cache, estou a garantir que os códigos que apontam para a mesma string apontam para a mesma memória, poupando-me assim o espaço RAM. Se as cordas estivessem na verdade recolhidas, não funcionaria para mim. Isto seria basicamente negar o propósito de internar. O meu não será recolhido porque tenho uma referência a cada fio do cache.
 0
Author: Rodney P. Barbati, 2013-08-16 00:20:07

O custo de prender uma corda é muito mais do que o tempo economizado em uma única stringA.é igual a (B) comparação. Use-o apenas (por razões de desempenho) quando estiver a usar repetidamente as mesmas variáveis de cadeia inalteradas. Por exemplo, se você iterar regularmente sobre uma lista estável de strings para atualizar alguns mapas riscados no mesmo campo de string, você pode obter uma boa gravação.

Eu sugeriria a utilização de string interning para ajustar o desempenho quando se está a optimizar partes específicas da sua codigo.

Lembra-te também que as cordas são imutáveis e não cometas o erro tolo de

String a = SOME_RANDOM_VALUE
a.intern()

Lembra-te de fazer

String a = SOME_RANDOM_VALUE.intern()
 0
Author: grumblebee, 2013-09-16 06:57:33

Se está à procura de um substituto ilimitado para o texto.interno, também lixo coletado, o seguinte está funcionando bem para mim.

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

Claro, se você puder estimar aproximadamente quantas cadeias de caracteres diferentes haverá, então simplesmente use String.intern () with-XX: StringTableSize= highEnoughValue .

 0
Author: bdruemen, 2016-11-04 14:22:16