1. Información general
El objeto String es la clase más utilizada en el lenguaje Java.
En este artículo rápido, exploraremos Java String Pool, la región de memoria especial donde las cadenas son almacenadas por la JVM .
2. Pasantía de cuerdas
Gracias a la inmutabilidad de Strings en Java, la JVM puede optimizar la cantidad de memoria asignada para ellos almacenando solo una copia de cada String literal en el grupo . Este proceso se llama pasantía .
Cuando creamos una variable de cadena y le asignamos un valor, la JVM busca en el grupo una cadena de igual valor.
Si lo encuentra, el compilador de Java simplemente devolverá una referencia a su dirección de memoria, sin asignar memoria adicional.
Si no se encuentra, se agregará al grupo (internado) y se devolverá su referencia.
Escribamos una pequeña prueba para verificar esto:
String constantString1 = "Baeldung"; String constantString2 = "Baeldung"; assertThat(constantString1) .isSameAs(constantString2);
3. Cadenas asignadas mediante el constructor
Cuando creamos una cadena a través del operador new , el compilador de Java creará un nuevo objeto y lo almacenará en el espacio de almacenamiento reservado para la JVM.
Cada cadena creada de esta manera apuntará a una región de memoria diferente con su propia dirección.
Veamos en qué se diferencia esto del caso anterior:
String constantString = "Baeldung"; String newString = new String("Baeldung"); assertThat(constantString).isNotSameAs(newString);
4. String Literal vs String Object
Cuando creamos un objeto String usando el operador new () , siempre crea un nuevo objeto en la memoria del montón. Por otro lado, si creamos un objeto usando una sintaxis literal de cadena, por ejemplo, "Baeldung", puede devolver un objeto existente del grupo de cadenas, si ya existe. De lo contrario, creará un nuevo objeto String y se colocará en el grupo de cadenas para su reutilización futura.
En un nivel alto, ambos son los objetos String , pero la principal diferencia proviene del punto en que el operador new () siempre crea un nuevo objeto String . Además, cuando creamos un String usando literal, está interno.
Esto será mucho más claro cuando comparemos dos objetos String creados usando String literal y el nuevo operador:
String first = "Baeldung"; String second = "Baeldung"; System.out.println(first == second); // True
En este ejemplo, los objetos String tendrán la misma referencia.
A continuación, creemos dos objetos diferentes usando new y verifiquemos que tengan referencias diferentes:
String third = new String("Baeldung"); String fourth = new String("Baeldung"); System.out.println(third == fourth); // False
De manera similar, cuando comparamos un literal String con un objeto String creado usando el operador new () usando el operador ==, devolverá falso:
String fifth = "Baeldung"; String sixth = new String("Baeldung"); System.out.println(fifth == sixth); // False
En general, deberíamos usar la notación literal String cuando sea posible . Es más fácil de leer y le da al compilador la oportunidad de optimizar nuestro código.
5. Pasantía manual
Podemos internar manualmente un String en Java String Pool llamando al método intern () en el objeto que queremos internar.
Al internar manualmente el String almacenará su referencia en el grupo, y la JVM devolverá esta referencia cuando sea necesario.
Creemos un caso de prueba para esto:
String constantString = "interned Baeldung"; String newString = new String("interned Baeldung"); assertThat(constantString).isNotSameAs(newString); String internedString = newString.intern(); assertThat(constantString) .isSameAs(internedString);
6. Recolección de basura
Antes de Java 7, la JVM colocaba Java String Pool en el espacio PermGen , que tiene un tamaño fijo; no se puede expandir en tiempo de ejecución y no es elegible para la recolección de basura .
El riesgo de internar Strings en PermGen (en lugar del Heap ) es que podemos obtener un error OutOfMemory de la JVM si internamos demasiadas Strings .
Desde Java 7 en adelante, la agrupación de cadenas de Java se almacena en el espacio del montón , que es la basura recolectada por la JVM . La ventaja de este enfoque es el riesgo reducido de error OutOfMemory porque las cadenas sin referencia se eliminarán del grupo, liberando así memoria.
7. Rendimiento y optimizaciones
En Java 6, la única optimización que podemos realizar es aumentar el espacio de PermGen durante la invocación del programa con la opción MaxPermSize JVM:
-XX:MaxPermSize=1G
En Java 7, tenemos opciones más detalladas para examinar y expandir / reducir el tamaño del grupo. Veamos las dos opciones para ver el tamaño de la piscina:
-XX:+PrintFlagsFinal
-XX:+PrintStringTableStatistics
Si queremos aumentar el tamaño del grupo en términos de depósitos, podemos usar la opción JVM StringTableSize :
-XX:StringTableSize=4901
Antes de Java 7u40, el tamaño del grupo predeterminado era 1009 depósitos, pero este valor estaba sujeto a algunos cambios en las versiones más recientes de Java. Para ser precisos, el tamaño de grupo predeterminado desde Java 7u40 hasta Java 11 era 60013 y ahora aumentó a 65536.
Tenga en cuenta que aumentar el tamaño del grupo consumirá más memoria, pero tiene la ventaja de reducir el tiempo necesario para insertar las cadenas en la tabla.
8. Una nota sobre Java 9
Hasta Java 8, las cadenas se representaban internamente como una matriz de caracteres, char [] , codificados en UTF-16 , de modo que cada carácter utiliza dos bytes de memoria.
Con Java 9 se proporciona una nueva representación, llamada Compact Strings. Este nuevo formato elegirá la codificación apropiada entre char [] y byte [] dependiendo del contenido almacenado.
Dado que la nueva representación de cadena utilizará la codificación UTF-16 solo cuando sea necesario, la cantidad de memoria del montón será significativamente menor, lo que a su vez causa menos sobrecarga del recolector de basura en la JVM.
9. Conclusión
En esta guía, mostramos cómo la JVM y el compilador de Java optimizan las asignaciones de memoria para los objetos String a través de Java String Pool.
Todos los ejemplos de código utilizados en el artículo están disponibles en GitHub.