[Java] Problemas de almacenamiento de cadenas y direcciones de memoria en JVM

descripción general

La dirección de memoria de String es un punto que a menudo se pregunta en las entrevistas sobre Java. Por ejemplo, ¿cuál es la diferencia entre un String copiado directamente y un String nuevo? ¿Cómo cambia la dirección durante la concatenación de cadenas? etc.
Para solucionar estos problemas de direcciones, primero debemos saber cómo se almacena String en la JVM.

1. Almacenamiento de objetos String en JVM

Primero dé la definición: las cadenas se almacenan en el grupo constante (Constant Pool)
en el área del método.¿Qué es el grupo constante? El grupo constante se genera durante la compilación y se utiliza para almacenar varios literales y referencias de símbolos generados por el compilador. Por ejemplo, int a = 8; String a = "abc"; 8 y "abc" se colocarán en el grupo constante durante la fase de compilación. Por supuesto, el grupo de constantes también se puede expandir durante el tiempo de ejecución para colocar nuevas constantes en el grupo. El método más utilizado es el método intern() de String, que se describirá en detalle más adelante.

Veamos este código simple nuevamente.

	String str = "aa";
	String str1 = "aa";
	String str2 = new String("aa");
	String str3 = new String("aa");
	System.out.println(str == str1);//true
	System.out.println(str2 == str3);//false
	System.out.println(str == str2);//false

¿Por qué es este el resultado?
De acuerdo con el orden de ejecución del programa, primero, "aa" como literal, es decir, una constante, se agregará al grupo de constantes durante la compilación, y luego la JVM asigna su dirección en el grupo de constantes a str ; cuando Cuando llega a str1, la JVM primero comprueba si hay una constante "aa" en el grupo de constantes. Dado que "aa" se ha creado en el grupo de constantes al asignar un valor a str, la JVM devuelve directamente esta dirección a str1. Por lo tanto, las direcciones de str y str1 son las mismas y el resultado es verdadero.

Llegando a str2 y str3, estos dos son Strings producidos por new y son objetos ¿Dónde se almacenan los objetos? Almacenado en el montón, por lo que esencialmente str2 y str3 almacenan las direcciones de estos dos objetos en el montón . new dos veces, son objetos diferentes, por lo que las direcciones de str2 y str3 no son las mismas y se devuelve false.

Ahora mire el último resultado, str == str2? Uno de ellos almacena la dirección en el grupo constante y el otro almacena la dirección en el montón. ¿Cómo pueden ser iguales? Devuelven falso.

2. Acerca del empalme de cuerdas

Veamos este fragmento de código.

	String a = "Hello2";
	String b = "Hello";
	final String c = "Hello";
	
	String d = b + "2";
	String e = c + "2";
	String f = "Hello" + "2";
	
	System.out.println(a==d);//false
	System.out.println(a==e);//true
	System.out.println(a==f);//true

En primer lugar, a, b y c son asignaciones literales, por lo que ahora hay dos cadenas "Hello2" y "Hello" en el grupo constante. Con base en los resultados anteriores podemos sacar las siguientes dos conclusiones:

(1) Al agregar cadenas, el resultado de la adición de cadenas estáticas se agregará al grupo constante . Si hay este resultado en el grupo constante, su referencia se devolverá directamente; si no, se creará la cadena y la referencia ser devuelto. Debido a que el grupo constante ahora ya tiene "Hello2", los valores de e y f son en realidad referencias a "Hello2" en el grupo constante, y a, e y f son todos iguales.

(2) Al agregar cadenas, si contienen variables, como b en d, no ingresarán al grupo constante . En DeBug en IDEA, forzamos la declaración String d = b + "2"; y encontrará que la capa inferior del programa en realidad crea primero un StringBuffer y luego llama al método append() para agregar b y "2". al StringBuffer, luego llame a toString() para devolver la cadena empalmada, y el código fuente final de toString() se ve así:

@Override
public String toString() {
    
    
    // Create a copy, don't share the array
    return new String(value, 0, count);
}

¡Míralo! ¡Devuelve una nueva cadena ! Por supuesto, la dirección de esta Cadena no será la misma que la de cualquier objeto existente.
Con respecto al empalme de cuerdas, simplemente distinga entre estas dos situaciones.

3. Acerca del método interno ()

Como mencionamos antes, la cadena nueva no se almacena directamente en el grupo constante, y la función del método intern () es agregar la cadena al grupo constante y luego devolver la dirección de la cadena en el grupo constante.

String str1 = "a";
String str2 = "b";
String str3 = "ab";
String str4 = str1 + str2;
String str5 = new String("ab");
 
System.out.println(str5 == str3);//false
System.out.println(str5.intern() == str3);//true
System.out.println(str5.intern() == str4);//false
System.out.println(str5.intern() == str4.intern());//true

Al llamar a str5.intern (), la JVM consulta el grupo constante para ver si existe la cadena "ab", debido a que se creó cuando a str3 se le asigna un valor, su dirección se devuelve directamente. str4 se obtiene sumando dos variables, por lo que equivale a venir de nuevo.

Otro punto que necesita atención es: si simplemente llama a str5.intern (), str5 en sí no cambiará, seguirá siendo la dirección en el montón. Si desea que str5 almacene la dirección en el grupo constante, debe cambiar str5.intern() Luego, el valor de retorno se asigna a str5. Puedes hacer las siguientes pruebas:

String str3 = "ab";
String str5 = new String("ab");
str5.intern();
System.out.println(str5 == str3);//false
str5 = str5.intern();
System.out.println(str5 == str3);//true

Supongo que te gusta

Origin blog.csdn.net/weixin_43390123/article/details/124376835
Recomendado
Clasificación