Guia para o conjunto de strings Java
1. Visão geral
O objetoString é a classe mais usada na linguagem Java.
Neste artigo rápido, vamos explorar o Java String Pool -the special memory region where Strings are stored by the JVM.
2. String Interning
Graças à imutabilidade deStrings em Java, a JVM pode otimizar a quantidade de memória alocada para eles emstoring only one copy of each literal String in the pool. Este processo é denominadointerning.
Quando criamos uma variávelString e atribuímos um valor a ela, a JVM procura no pool porString de igual valor.
Se encontrado, o compilador Java simplesmente retornará uma referência ao seu endereço de memória, sem alocar memória adicional.
Se não for encontrado, ele será adicionado ao pool (interno) e sua referência será retornada.
Vamos escrever um pequeno teste para verificar isso:
String constantString1 = "example";
String constantString2 = "example";
assertThat(constantString1)
.isSameAs(constantString2);
3. Strings alocado usando o construtor
Quando criamos umString por meio do operadornew, o compilador Java cria um novo objeto e o armazena no espaço de heap reservado para a JVM.
CadaString criado assim apontará para uma região de memória diferente com seu próprio endereço.
Vamos ver como isso é diferente do caso anterior:
String constantString = "example";
String newString = new String("example");
assertThat(constantString).isNotSameAs(newString);
4. String Literal vsString Object
When we create a String object using the new() operator, it always creates a new object in heap memory. On the other hand, if we create an object using String literal syntax e.g. “example”, it may return an existing object from the String pool, if it already exists. Caso contrário, ele criará um novo objeto String e o colocará no pool de strings para reutilização futura.
Em um nível alto, ambos são objetosString, mas a principal diferença vem do ponto que o operadornew() sempre cria um novo objetoString. Além disso, quando criamos umString usando literal - ele é interno.
Isso ficará muito mais claro quando compararmos dois objetosString criados usando o literalString e o operadornew:
String first = "example";
String second = "example";
System.out.println(first == second); // True
Neste exemplo, os objetosString terão a mesma referência.
A seguir, vamos criar dois objetos diferentes usandonewe verificar se eles têm referências diferentes:
String third = new String("example");
String fourth = new String("example");
System.out.println(third == fourth); // False
Da mesma forma, quando comparamos um literalString com um objetoString criado usando o operadornew() usando o operador ==, ele retornaráfalse:
String fifth = "example";
String sixth = new String("example");
System.out.println(fifth == sixth); // False
Em geral,we should use the String literal notation when possible. É mais fácil de ler e oferece ao compilador a chance de otimizar nosso código.
5. Internação Manual
Podemos internar manualmente umString no Java String Pool chamando o métodointern() no objeto que queremos internar.
A internação manual deString armazenará sua referência no pool e a JVM retornará essa referência quando necessário.
Vamos criar um caso de teste para isso:
String constantString = "interned example";
String newString = new String("interned example");
assertThat(constantString).isNotSameAs(newString);
String internedString = newString.intern();
assertThat(constantString)
.isSameAs(internedString);
6. Coleta de lixo
Antes do Java 7, o JVMplaced the Java String Pool in the PermGen space, which has a fixed size — it can’t be expanded at runtime and is not eligible for garbage collection.
O risco de internarStrings emPermGen (em vez deHeap) é quewe can get an OutOfMemory error da JVM se internarmos muitosStrings.
A partir do Java 7 em diante, o Java String Pool éstored in the Heap space, which is garbage collected pelo JVM. A vantagem dessa abordagem é oreduced risk of OutOfMemory error porqueStrings não referenciado será removido do pool, portanto liberando memória.
7. Desempenho e otimizações
No Java 6, a única otimização que podemos realizar é aumentar o espaçoPermGen durante a invocação do programa com a opção JVMMaxPermSize:
-XX:MaxPermSize=1G
No Java 7, temos opções mais detalhadas para examinar e expandir / reduzir o tamanho do conjunto. Vamos ver as duas opções para visualizar o tamanho da piscina:
-XX:+PrintFlagsFinal
-XX:+PrintStringTableStatistics
Se quisermos aumentar o tamanho do pool em termos de depósitos, podemos usar a opçãoStringTableSize JVM:
-XX:StringTableSize=4901
Antes do Java 7u40, o tamanho padrão do pool era de 1009 buckets, mas esse valor estava sujeito a algumas alterações nas versões Java mais recentes. Para ser preciso, o tamanho do conjunto padrão do Java 7u40 até o Java 11 era 60013 e agora aumentou para 65536.
Observe que aumentar o tamanho do pool consumirá mais memória, mas tem a vantagem de reduzir o tempo necessário para inserirStrings na tabela.
8. Uma nota sobre o Java 9
Até o Java 8,Strings era internamente representado como um array de caracteres -char[], codificado emUTF-16, de modo que cada caractere usa dois bytes de memória.
Com o Java 9, uma nova representação é fornecida, chamadaCompact Strings.. Este novo formato escolherá a codificação apropriada entrechar[]ebyte[] dependendo do conteúdo armazenado.
Uma vez que a nova representaçãoString usará a codificaçãoUTF-16 apenas quando necessário, a quantidade de memóriaheap será significativamente menor, o que por sua vez causa menos sobrecarga deGarbage Collector naJVM.
9. Conclusão
Neste guia, mostramos como a JVM e o compilador Java otimizam as alocações de memória para objetosString por meio do Java String Pool.
Todos os exemplos de código usados no artigo estão disponíveisover on GitHub.