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()
?
Respostas:
quando você precisar de velocidade, pois você pode comparar seqüências de caracteres por referência (== é mais rápido que igual)
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)
fonte
if (s1.equals(s2))
eif (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.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).fonte
str.intern()
quandostr
estiver"Hello"
.String.intern()
é definitivamente lixo coletado em JVMs modernas.O seguinte NUNCA fica sem memória devido à atividade do GC:
Veja mais (de mim) sobre o mito da String.intern não GCed () .
fonte
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 ... :-)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:
String.intern()
no Java 6, porque ele entra no PermGenString.intern()
em Java 7 e Java 8: usa 4-5x menos memória do que rolar seu próprio pool de objetos-XX:StringTableSize
(o padrão provavelmente é muito pequeno; defina um número primo)fonte
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 ...
fonte
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 ojava.lang.String
tipo 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.
fonte
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 ()
fonte
intern
, e muito boas razões queequals
não fazê-lo por padrão. O link que você postou é besteira completa. O último parágrafo até admite queintern
tenha 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.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:
fonte
String
instâncias. Ao analisar o conteúdo, vi muitas duplicatas e decidi mudarintern()
, o que economizou centenas de MB.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_info
estrutura dedicada (diferente da maioria dos outros objetos que têm representações mais genéricas).JVMS
O JVMS 7 5.1 diz :
Bytecode
Também é instrutivo examinar a implementação de bytecode no OpenJDK 7.
Se decompilarmos:
temos na piscina constante:
e
main
:Observe como:
0
e3
: a mesmaldc #2
constante é carregada (os literais)12
: uma nova instância de string é criada (com#2
como argumento)35
:a
ec
são comparados como objetos regulares comif_acmpne
A representação de strings constantes é bastante mágica no bytecode:
new String
)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 ConstantValueldc
Bônus : compare isso ao pool Inteiro , que não possui suporte direto ao bytecode (isto é, sem
CONSTANT_String_info
analógico).fonte
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.
fonte
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ávelsubString(...)
e não tem controle sobre o responsável pela chamada, considere armazenar osintern()
argumentos String passados para o construtor. Isso libera o buffer grande em potencial.fonte
A internação de strings é útil no caso em que o
equals()
método está sendo chamado frequentemente, porque oequals()
método faz uma verificação rápida para verificar se os objetos são os mesmos no início do método.Isso geralmente ocorre quando a pesquisa por um
Collection
outro 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.
fonte
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.
fonte
http://kohlerm.blogspot.co.uk/2009/01/is-javalangstringintern-really-evil.html
afirma que
String.equals()
costuma"=="
compararString
objetos antes, de acordo comhttp://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 dessasequals()
operações será String a String, de modo que o teste é quase sempre passado. De fato, priorizar "==" por dentroString.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":
Mas se você mudar
i
parai.toString()
a segunda linha, é claro que étrue
.Locais onde você pode esperar um benefício com a internação incluem
Set
eMap
, 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 nointern()
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!fonte
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 ()
fonte
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)
fonte
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:
Uso isso frequentemente quando leio campos de fluxos ou de ResultSets. Nota:
LRUCache
é um cache simples baseado emLinkedHashMap<K,V>
. Chama automaticamente oretrieve()
método fornecido pelo usuário para todas as falhas de cache.A maneira de usar isso é criar um
LRUInternalizer
antes da leitura (ou leituras), usá-lo para internalizar Strings e outros pequenos objetos imutáveis e liberá-lo. Por exemplo:fonte
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.
fonte
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
lembre-se de fazer
fonte
Se você está procurando um substituto ilimitado para o String.intern, também coletado como lixo, o seguinte está funcionando bem para mim.
Obviamente, se você puder estimar aproximadamente quantas strings diferentes, basta usar String.intern () com -XX: StringTableSize = highEnoughValue .
fonte