É uma boa prática usar java.lang.String.intern ()?

194

O Javadoc String.intern()não dá muitos detalhes. (Resumindo: ele retorna uma representação canônica da string, permitindo a comparação de strings internas ==)

  • Quando eu usaria essa função em favor de String.equals()?
  • Existem efeitos colaterais não mencionados no Javadoc, ou seja, mais ou menos otimização pelo compilador JIT?
  • Existem outros usos de String.intern()?
Daniel Rikowski
fonte
14
A chamada intern () tem seu próprio impacto no desempenho; o uso de intern () para melhorar o desempenho precisa ser testado para garantir que ele realmente acelere significativamente o seu programa, valendo a complexidade extra. Você também pode usar isso para reduzir o consumo de memória para tabelas grandes com valores repetidamente confiáveis. No entanto, em ambos os casos, existem outras opções que podem ser melhores.
Peter Lawrey
Sim, intern () tem seu próprio impacto no desempenho. Especialmente porque o custo intern () aumenta linearmente à medida que você estica as strings e mantém uma referência a elas. Pelo menos em um sol / oracle 1.6.0_30 vm.
lacroix1547

Respostas:

125

Quando eu usaria essa função em favor de String.equals ()

quando você precisar de velocidade, pois você pode comparar seqüências de caracteres por referência (== é mais rápido que igual)

Existem efeitos colaterais não mencionados no Javadoc?

A principal desvantagem é que você deve se lembrar de ter intern () todas as strings que você irá comparar. É fácil esquecer de estagiar () todas as seqüências e, em seguida, você pode obter resultados incorretamente confusos. Além disso, para o bem de todos, lembre-se de documentar claramente que está confiando nas seqüências de caracteres que estão sendo internalizadas.

A segunda desvantagem, se você decidir internalizar seqüências, é que o método intern () é relativamente caro. Ele precisa gerenciar o pool de cadeias únicas para que ele trabalhe bastante (mesmo que a cadeia já tenha sido internalizada). Portanto, tenha cuidado no design do seu código, por exemplo, intern () todas as strings apropriadas na entrada para que você não precise mais se preocupar com isso.

(de JGuru)

Terceira desvantagem (somente Java 7 ou menos): Strings internadas vivem no espaço PermGen, que geralmente é bem pequeno; você pode encontrar um OutOfMemoryError com bastante espaço livre no heap.

(de Michael Borgwardt)

dfa
fonte
64
Uma terceira desvantagem: Strings internadas vivem no espaço PermGen, que geralmente é bem pequeno; você pode encontrar um OutOfMemoryError com bastante espaço livre no heap.
227 Michael Borgwardt
15
As VMs mais recentes da AFAIK também coletam lixo no espaço PermGen.
217 Daniel Rikowski
31
O estagiário é sobre gerenciamento de memória, não velocidade de comparação. A diferença entre if (s1.equals(s2))e if (i1 == i2)é mínima, a menos que você tenha muitas seqüências longas com os mesmos caracteres principais. Na maioria dos usos no mundo real (exceto URLs), as strings diferem nos primeiros caracteres. E longas cadeias if-else são um cheiro de código de qualquer maneira: use enumerações e mapas de funções.
kdgregory
25
você ainda pode usar sintaxe s1.equals todo o seu programa, o uso NÃO ==, .equals usar == internamente para curto-circuito avaliação
gtrak
15
Michael Borgwardt NÃO disse que seqüências de caracteres internas não podem ser coletadas como lixo. E isso é uma afirmação falsa. O que os comentários de Michael (corretamente) dizem é mais sutil do que isso.
Stephen C
193

Isso tem (quase) nada a ver com comparação de strings. A internação de cadeias destina-se a economizar memória se você tiver muitas cadeias com o mesmo conteúdo em seu aplicativo. O uso String.intern()do aplicativo terá apenas uma instância a longo prazo e um efeito colateral é que você pode executar uma comparação rápida de igualdade de referência em vez da comparação de cadeias comuns (mas isso geralmente não é aconselhável porque é muito fácil interromper, esquecendo-se de apenas internar uma única instância).

Daniel Brückner
fonte
4
Isso não está certo. A internação de strings ocorre sempre, automaticamente, quando cada expressão de string é avaliada. Sempre há uma cópia para cada sequência de caracteres usada e é "compartilhada internamente" se ocorrerem vários usos. Chamar String.intern () não faz tudo isso acontecer - apenas retorna a representação canônica interna. Veja javadoc.
Glen Best
16
É necessário esclarecer - a internação sempre acontece automaticamente para Strings constantes em tempo de compilação (literais e expressões fixas). Além disso, ocorre quando String.intern () é chamado em Strings avaliadas dinamicamente em tempo de execução.
Glen Best
Então você quer dizer, se houver 1000 objetos "Hello" no Heap e eu executar intern () em um deles, o resto 999 objetos serão destruídos automaticamente?
Arun Raaj #
@ArunRaaj não, você terá seu 1000 ainda na pilha e um extra no pool interno, que poderá estar pronto para reutilização mais tarde, str.intern()quando strestiver "Hello".
Matthieu
37

String.intern()é definitivamente lixo coletado em JVMs modernas.
O seguinte NUNCA fica sem memória devido à atividade do 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 da String.intern não GCed () .

Gili Nachum
fonte
26
OutOfMemoryException- Não, não é o código acima, no meu cérebro : link para o artigo javaturning, que está a apontar para este artigo, que está apontando para o artigo javaturning, que ... :-)
user85421
Embora você pode ver que a mensagem foi editada para adicionar esse link;)
Riking
3
Você pode mencionar que também é o autor da referência externa à qual está vinculado.
Thorbjørn Ravn Andersen
11
@Carlos ligando uma referência externa que links para voltar ao stackoverflow deve causar a .. Stackoverflow :)
Seiti
2
@Seiti As referências circulares são facilmente detectadas nos dias de hoje: p
Ajay
16

Eu escrevi recentemente um artigo sobre a implementação de String.intern () em Java 6, 7 e 8: String.intern em Java 6, 7 e 8 - pool de strings .

Espero que ele contenha informações suficientes sobre a situação atual com o pool de strings em Java.

Em poucas palavras:

  • Evite String.intern()no Java 6, porque ele entra no PermGen
  • Prefere String.intern()em Java 7 e Java 8: usa 4-5x menos memória do que rolar seu próprio pool de objetos
  • Certifique-se de ajustar -XX:StringTableSize(o padrão provavelmente é muito pequeno; defina um número primo)
mik1
fonte
3
Por favor, não basta postar links para o seu blog, isso é considerado por alguns como SPAM. Além disso, os links de blogs têm uma tendência notável a morrer 404. Resuma o artigo aqui inline aqui ou deixe esse link em um comentário para a pergunta.
Mat
3
Obrigado por escrever que @ mik1! Artigo muito informativo, claro e atualizado. (Eu vim para cá com a intenção de postar um link para ele próprio.)
Luke Usherwood
1
Obrigado por mencionar o argumento -XX. Você também pode usar isso para ver as estatísticas da tabela: -XX: + PrintStringTableStatistics
csadler
13

Comparar cadeias de caracteres com == é muito mais rápido que com equals ()

5 Tempo mais rápido, mas como a comparação de cadeias geralmente representa apenas uma pequena porcentagem do tempo total de execução de um aplicativo, o ganho geral é muito menor que isso, e o ganho final será diluído em alguns por cento.

String.intern () retire a string do Heap e coloque-a no PermGen

As cadeias internalizadas são colocadas em uma área de armazenamento diferente: Permanent Generation, que é uma área da JVM reservada para objetos não-usuários, como Classes, Methods e outros objetos internos da JVM. O tamanho desta área é limitado e é muito precioso que o monte. Sendo essa área menor que Heap, há mais probabilidade de usar todo o espaço e obter uma OutOfMemoryException.

String.intern () string são lixo coletado

Nas novas versões da JVM, as cadeias internalizadas também são coletadas como lixo quando não são referenciadas por nenhum objeto.

Tendo em mente o ponto 3 acima, você pode deduzir que String intern () pode ser útil apenas em algumas situações quando você faz muitas comparações de string; no entanto, é melhor não usar string interna se não souber exatamente o que deseja. estão fazendo ...

aleroot
fonte
1
Apenas para adicionar, as exceções de memória da pilha às vezes podem ser recuperadas, especialmente em modelos encadeados, como aplicativos da web. Quando o permgen é esgotado, um aplicativo normalmente fica permanentemente inoperante e, muitas vezes, trava recursos até ser morto.
19413 Taylor
7

Quando eu usaria essa função em favor de String.equals ()

Dado que eles fazem coisas diferentes, provavelmente nunca.

Internar seqüências de caracteres por razões de desempenho, para que você possa compará-las para igualdade de referência só será benéfico se você estiver mantendo referências às seqüências de caracteres por um tempo - seqüências de caracteres provenientes da entrada do usuário ou IO não serão internadas.

Isso significa que em seu aplicativo você recebe entrada de uma fonte externa e a processa em um objeto que possui um valor semântico - digamos, um identificador -, mas esse objeto tem um tipo indistinguível dos dados brutos e possui regras diferentes sobre como o programador deve use-o.

É quase sempre melhor criar um UserId tipo que é internado (é fácil criar um mecanismo de internação genérico seguro para threads) e age como uma enumeração aberta, do que sobrecarregar o java.lang.Stringtipo com semântica de referência, se for um ID do usuário.

Dessa forma, você não fica confuso entre se uma String específica foi ou não internada e pode encapsular qualquer comportamento adicional necessário na enumeração aberta.

Pete Kirkham
fonte
6

Não estou ciente de nenhuma vantagem, e se houvesse em alguém pensaria que igual () usaria intern () internamente (o que não é verdade).

Rebentando mitos internos ()

objetos
fonte
7
Apesar de você dizer que você não está ciente de quaisquer vantagens, o seu postou comparação identifica ligados através de == como sendo 5x mais rápido e, portanto, importante para o código performance texto-centric
Brian Agnew
3
Quando você tem muita comparação de texto para fazer, acabará ficando sem espaço no PermGen. Quando não há tanta comparação de texto para fazer, a diferença de velocidade não importa. De qualquer forma, apenas não estique () suas strings. Não vale a pena.
7609 Bombe
Também continua dizendo que o ganho relativo geral será tipicamente pequeno.
objetos
Não acho que esse tipo de lógica seja válida. Bom link!
217 Daniel Rikowski
1
@DR: que lógica? Essa é uma grande falácia. Objetos @: desculpe, mas seus argumentos ficam aquém dos motivos. Há muito boas razões para utilizar intern, e muito boas razões que equalsnão fazê-lo por padrão. O link que você postou é besteira completa. O último parágrafo até admite que interntenha um cenário de uso válido: processamento de texto pesado (por exemplo, um analisador). Concluir que “[XYZ] é perigoso se você não sabe o que está fazendo” é tão banal que machuca fisicamente.
Konrad Rudolph
4

Daniel Brückner está absolutamente certo. A internação de cadeias destina-se a economizar memória (heap).Atualmente, nosso sistema possui um hashmap gigante para armazenar determinados dados. À medida que o sistema é dimensionado, o hashmap será grande o suficiente para deixar a pilha sem memória (como testamos). Internando todas as seqüências duplicadas e todos os objetos no mapa de hash, economiza uma quantidade significativa de espaço de heap.

Também no Java 7, as seqüências de caracteres internas não vivem mais no PermGen, mas sim no heap. Portanto, você não precisa se preocupar com o tamanho e sim com o lixo coletado:

No JDK 7, as cadeias internas não são mais alocadas na geração permanente do heap Java, mas são alocadas na parte principal do heap Java (conhecida como gerações novas e antigas), juntamente com os outros objetos criados pelo aplicativo . Essa alteração resultará em mais dados residindo no heap Java principal e menos dados na geração permanente e, portanto, pode exigir que os tamanhos de heap sejam ajustados. A maioria dos aplicativos verá apenas diferenças relativamente pequenas no uso de heap devido a essa alteração, mas aplicativos maiores que carregam muitas classes ou fazem uso pesado do método String.intern () verão diferenças mais significativas.

xli
fonte
Preciso enfatizar que: no meu software, um despejo de heap mostrou que a maior parte do espaço de heap era usada por Stringinstâncias. Ao analisar o conteúdo, vi muitas duplicatas e decidi mudar intern(), o que economizou centenas de MB.
Matthieu
4

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 há suporte direto de bytecode para o pool de strings , que é implementado de maneira mágica e eficiente com uma CONSTANT_String_infoestrutura dedicada (diferente da maioria dos outros objetos que têm representações mais genéricas).

JVMS

O JVMS 7 5.1 diz :

Um literal de string é uma referência a uma instância da classe String e é derivado de uma estrutura CONSTANT_String_info (§4.4.3) na representação binária de uma classe ou interface. A estrutura CONSTANT_String_info fornece 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 (ou seja, literais que contenham a mesma sequência de pontos de código) se refiram à mesma instância da classe String (JLS §3.10.5). Além disso, se o método String.intern for chamado em qualquer string, o resultado será uma referência à mesma instância de classe que seria retornada se essa string aparecesse como um literal. Portanto, a seguinte expressão deve ter o valor true:

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

Para derivar um literal de cadeia, a Java Virtual Machine examina a sequência de pontos de código fornecidos pela estrutura CONSTANT_String_info.

  • Se o método String.intern já foi chamado em uma instância da classe String que contém uma sequência de pontos de código Unicode idênticos àqueles fornecidos pela estrutura CONSTANT_String_info, o resultado da derivação literal da string é uma referência à mesma instância da classe String.

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

Bytecode

Também é instrutivo examinar a implementação de bytecode no OpenJDK 7.

Se decompilarmos:

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 na 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

Observe como:

  • 0e 3: a mesma ldc #2constante é carregada (os literais)
  • 12: uma nova instância de string é criada (com #2como argumento)
  • 35: ae csão comparados como objetos regulares comif_acmpne

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

  • possui uma estrutura CONSTANT_String_info dedicada , diferente de objetos regulares (por exemplo new String)
  • o struct aponta para uma estrutura CONSTANT_Utf8_info que contém os dados. Esses são os únicos dados necessários para representar a sequência.

e a citação da JVMS acima parece dizer que sempre que o Utf8 apontado é o mesmo, instâncias idênticas são carregadas ldc.

Eu fiz testes semelhantes para campos e:

  • static final String s = "abc"aponta para a tabela constante por meio do atributo ConstantValue
  • campos não finais não possuem esse atributo, mas ainda podem ser inicializados com ldc

Bônus : compare isso ao pool Inteiro , que não possui suporte direto ao bytecode (isto é, sem CONSTANT_String_infoanalógico).

Ciro Santilli adicionou uma nova foto
fonte
2

Eu examinaria intern e == - comparação em vez de igual apenas no caso de igual-comparação sendo gargalo em múltiplas comparações de string. É altamente improvável que ajude com um pequeno número de comparações, porque intern () não é gratuito. Depois de inserir agressivamente seqüências de caracteres, você encontrará chamadas para intern () cada vez mais lentas.

Mikko Maunu
fonte
2

Um tipo de vazamento de memória pode advir do uso de subString()quando o resultado é pequeno comparado à cadeia de origem e o objeto tem uma vida útil longa.

A solução normal é usar, new String( s.subString(...))mas quando você tem uma classe que armazena o resultado de um potencial / provável subString(...)e não tem controle sobre o responsável pela chamada, considere armazenar os intern()argumentos String passados ​​para o construtor. Isso libera o buffer grande em potencial.

eremmel
fonte
Interessante, mas talvez isso dependa da implementação.
akostadinov 23/08
1
O vazamento de memória em potencial mencionado acima não ocorre no java 1.8 e 1.7.06 (e mais recente), consulte Alterações na representação interna da String feita no Java 1.7.0_06 .
eremmel
que confirma que as micro otimizações devem ser aplicadas somente quando necessário após um desempenho e / ou perfil de memória. Obrigado.
akostadinov
2

A internação de strings é útil no caso em que o equals()método está sendo chamado frequentemente, porque o equals()método faz uma verificação rápida para verificar se os objetos são os mesmos no início do método.

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

Isso geralmente ocorre quando a pesquisa por um Collectionoutro código também pode fazer verificações de igualdade de string.

Há um custo envolvido na internação, porém, realizei uma marca de microssegundo de algum código e descobri que o processo de internação aumenta o tempo de execução em um fator de 10.

O melhor local para realizar a internação é geralmente quando você está lendo chaves armazenadas fora do código, pois as cadeias de caracteres no código são internadas automaticamente. Isso normalmente aconteceria nos estágios de inicialização do seu aplicativo, a fim de evitar a penalidade do primeiro usuário.

Outro local em que isso pode ser feito é ao processar a entrada do usuário que pode ser usada para realizar as principais pesquisas. Isso normalmente acontece no seu processador de solicitações, observe que as seqüências internas devem ser transmitidas.

Além disso, não faz muito sentido internar no restante do código, pois geralmente não trará nenhum benefício.

Archimedes Trajano
fonte
1

Eu votaria a favor, não valendo o trabalho de manutenção.

Na maioria das vezes, não haverá necessidade nem benefício de desempenho, a menos que seu código trabalhe muito com substrings. Nesse caso, a classe String usará a string original mais um deslocamento para economizar memória. Se o seu código usa muito substrings, suspeito que isso fará com que seus requisitos de memória explodam.

wm_eddie
fonte
1

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

afirma que String.equals()costuma "=="comparar Stringobjetos antes, de acordo com

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

ele compara os comprimentos de Strings e, em seguida, o conteúdo.

(A propósito, as sequências de código de produto em um catálogo de vendas podem ter o mesmo comprimento - o BIC0417 é o capacete de segurança de um ciclista, o TIG0003 é um tigre adulto masculino - você provavelmente precisará de todos os tipos de licenças para solicitar um deles. talvez seja melhor encomendar um capacete de segurança ao mesmo tempo.)

Portanto, parece que você se beneficia de substituir o Strings pela intern()versão deles , mas obtém segurança - e legibilidade e conformidade padrão - sem usar o uso de "==" equals()na sua programação. E a maior parte do que vou dizer depende disso, se for verdade.

Mas String.equals()teste se você passou uma String e não outro objeto antes de usar "=="? Não estou qualificado para dizer, mas acho que não, porque a maioria dessas equals()operações será String a String, de modo que o teste é quase sempre passado. De fato, priorizar "==" por dentro String.equals()implica a confiança de que você frequentemente está comparando a String ao mesmo objeto real.

Espero que ninguém fique surpreso que as seguintes linhas produzam um resultado "falso":

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

Mas se você mudar ipara i.toString()a segunda linha, é claro que é true.

Locais onde você pode esperar um benefício com a internação incluem Sete Map, obviamente. Espero que as strings internas tenham seus hashcodes em cache ... Eu acho que isso seria um requisito. E espero não ter revelado apenas uma idéia que poderia me render um milhão de dólares. :-)

Quanto à memória, também é óbvio que esse é um limite importante se o seu volume de Strings for grande ou se você desejar que a memória usada pelo código do programa seja muito pequena. Se o seu volume de -distinct- Strings for muito grande, talvez seja hora de considerar o uso de código de programa de banco de dados dedicado para gerenciá-los e um servidor de banco de dados separado. Da mesma forma, se você pode melhorar um programa pequeno (que precisa ser executado em 10000 instâncias simultaneamente), ele não armazena suas próprias Strings.

Parece um desperdício criar uma nova String e descartá-la imediatamente por seu intern()substituto, mas não há uma alternativa clara, exceto por manter a String duplicada. Então, na verdade, o custo de execução é procurar sua string no pool interno e permitir que o coletor de lixo descarte a original. E se é uma string literal, ela já está internada de qualquer maneira.

Eu estou querendo saber se intern()pode ser abusado por código de programa malicioso para detectar se alguma String e suas referências de objeto já existem no intern()pool e, portanto, existem em outros lugares da sessão Java, quando isso não deveria ser conhecido. Mas isso só seria possível quando o código do programa já estiver sendo usado de maneira confiável, eu acho. Ainda assim, é algo a considerar sobre as bibliotecas de terceiros que você inclui no seu programa para armazenar e lembrar seus números de PIN do caixa eletrônico!

Robert Carnegie
fonte
0

O verdadeiro motivo para usar estagiário não é o acima. Você pode usá-lo após obter um erro de falta de memória. Muitas strings de um programa típico são String.substring () de outras strings grandes [pense em remover um nome de usuário de um arquivo xml de 100K. A implementação do java é que, a substring mantém uma referência à string original e o início + fim dessa string enorme. (O pensamento por trás disso é uma reutilização da mesma grande corda)

Após 1000 arquivos grandes, dos quais você salva apenas 1000 nomes abreviados, você manterá na memória os 1000 arquivos inteiros! Solução: neste cenário, basta usar smallsubstring.intern ()

asaf
fonte
Por que não criar uma nova string a partir da substring, se você precisar?
Thorbjørn Ravn Andersen
0

Estou usando o intern para economizar memória, tenho uma grande quantidade de dados String na memória e estou mudando para usar o intern () economizado uma quantidade enorme de memória. Infelizmente, embora use muito menos memória, a memória usada é armazenada na memória PermGen e não na Heap e é difícil explicar aos clientes como aumentar a alocação desse tipo de memória.

Portanto, existe uma alternativa ao intern () para reduzir o consumo de memória (os benefícios de desempenho == versus iguais não são um problema para mim)

Paul Taylor
fonte
0

Vamos ser sinceros: o principal cenário de caso de uso é quando você lê um fluxo de dados (por meio de um fluxo de entrada ou de um JDBC ResultSet) e há uma infinidade de pequenas Strings que são repetidas o tempo todo.

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 frequentemente quando leio campos de fluxos ou de ResultSets. Nota: LRUCacheé um cache simples baseado em LinkedHashMap<K,V>. Chama automaticamente o retrieve()método fornecido pelo usuário para todas as falhas de cache.

A maneira de usar isso é criar um LRUInternalizerantes da leitura (ou leituras), usá-lo para internalizar Strings e outros pequenos objetos imutáveis ​​e liberá-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...
}
Pierre D
fonte
0

Estou usando-o para armazenar em cache o conteúdo de aproximadamente 36000 códigos vinculados a nomes associados. Interno as strings no cache, porque muitos dos códigos apontam para a mesma string.

Internando as seqüências de caracteres no meu cache, garanto que os códigos que apontam para a mesma sequência realmente apontam para a mesma memória, economizando, assim, espaço de RAM.

Se as seqüências internas fossem realmente coletadas de lixo, não funcionaria para mim. Isso basicamente negaria o propósito de internar. O meu não será coletado como lixo, porque estou mantendo uma referência a todas as strings no cache.

Rodney P. Barbati
fonte
Não, todas as seqüências iguais armazenadas na memória em um determinado momento ainda serão o mesmo objeto. Será um objeto diferente da sequência igual que estava na memória antes de ser coletada. Mas isso não é problema porque a cadeia antiga não está mais lá.
precisa saber é o seguinte
0

O custo de internar uma string é muito mais do que o tempo economizado em uma única comparação de stringA.equals (B). Use-o apenas (por razões de desempenho) quando estiver usando repetidamente as mesmas variáveis ​​de sequência inalteradas. Por exemplo, se você iterar regularmente sobre uma lista estável de seqüências de caracteres para atualizar alguns mapas digitados no mesmo campo, poderá obter uma boa economia.

Eu sugeriria o uso de cadeia de caracteres para ajustar o desempenho ao otimizar partes específicas do seu código.

Lembre-se também de que String é imutável e não cometa o erro bobo de

String a = SOME_RANDOM_VALUE
a.intern()

lembre-se de fazer

String a = SOME_RANDOM_VALUE.intern()
resmungão
fonte
0

Se você está procurando um substituto ilimitado para o String.intern, também coletado como lixo, 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;
    }
}

Obviamente, se você puder estimar aproximadamente quantas strings diferentes, basta usar String.intern () com -XX: StringTableSize = highEnoughValue .

bdruemen
fonte
O SoftRef faria mais sentido.
Vach
@vach Ao usar WeakReference (em vez de SoftReference), a memória é liberada anteriormente, para que outras alocações sejam mais rápidas. Depende do que mais o aplicativo está fazendo, qualquer um pode fazer sentido.
precisa saber é o seguinte