A menudo puede ver discusiones sobre la asignación de memoria de las cadenas Java cuando se ejecuta en secciones principales en Internet, como: String A = "123", String B = New String ("123"), ¿Dónde están estas dos formas de cuerdas almacenadas? De hecho, el valor literal de la cadena "123" en estas dos formas no se almacena en la pila ni en el montón en tiempo de ejecución. Se almacenan en un área constante en el área del método, y solo una copia se retiene en la memoria para el mismo valor literal de cadena. Lo analizaremos con ejemplos a continuación.
1. El operador == se usa en dos casos donde se comparan dos comparaciones de referencia de cadena:
public class StringTest {public static void main (String [] args) {// Parte 1 String S1 = "I Love China"; String S2 = "I Love China"; System.out.println ("Resultado:" + S1 == S2); // El resultado de ejecución del programa es verdadero // Parte 2 String S3 = New String ("Amo a China"); Cadena S4 = nueva cadena ("Amo a China"); System.out.println ("Resultado:" + S3 == S4); // El resultado de ejecución del programa es falso}}Sabemos que el operador == en Java compara los valores de las variables. El valor de la variable correspondiente al tipo de referencia almacena la dirección del objeto de referencia. Aquí la cadena es el tipo de referencia, y los valores de las cuatro variables aquí realmente almacenan las direcciones de la cadena. El resultado de ejecución para Parte2 es obvio, porque el nuevo operador hará que JVM cree nuevos objetos en el montón en tiempo de ejecución, y las direcciones de los dos objetos diferentes son diferentes. Sin embargo, a partir del resultado de la ejecución de Parte1, se puede ver que S1 y S2 son la misma dirección señalada. Entonces, ¿dónde se señalan las cuerdas por las variables S1 y S2 almacenadas? ¿Cómo maneja JVM las cadenas? Del mismo modo, para diferentes objetos de cadena en el montón apuntados por las variables S3 y S4, ¿guardarán una cuerda "Amo a China" en su propio espacio de objetos? Para comprender cómo JVM maneja las cadenas, primero miramos las instrucciones de Bytecode generadas por el compilador Java. A través de la instrucción Bytecode, analizamos qué operaciones realizarán JVM.
2. La siguiente es alguna información de Bytecode generada por el programa. El rojo marca la parte a la que debemos prestar atención.
Grupo constante: #1 = class #2 // stringtest #2 = utf8 stringtest #3 = class #4 // java/lang/object #4 = utf8 java/lang/object #5 = utf8 <Init> #6 = utf8 () v #7 = utf8 código #8 = MethodRef #3. #9 // java/lang/object ". NameAndType #5: #6 // "<Init>" :() V #10 = UTF8 LinenumberTable #11 = UTF8 LocalVariaBletable #12 = UTF8 this #13 = UTF8 LStringTest; #14 = UTF8 Main #15 = UTF8 ([ljava/lang/string;) v #16 = String #17 // Me encanta la referencia de China a la dirección de cadena #17 = UTF8 I Love China #18 = Fieldref #19. #21 // java/lang/system.out: ljava/io/printStEAM; #19 = Clase #20 // java/lang/sistema #20 = utf8 java/lang/sistema #21 = nameandtype #22: #23 // out: ljava/io/printstream; #22 = UTF8 OUT #23 = UTF8 ljava/io/printStream; #24 = Clase #25 // java/lang/stringBuilder #25 = utf8 java/lang/stringBuilder #26 = string #27 // resultado: #27 = utf8 resultado: #28 = Methodref #24. #29 // java/lang/stringBuilder. "<Init>" :( lJava/lang/string;) v #29 = nameandtype #5: "<Init>" :( ljava/lang/string;) v#30 = utf8 (ljava/lang/string;) v#31 = MethodRef#24.#32 // java/lang/stringBuilder.append: (z) ljava/lang/stringBuilder;#32 = nameandtype#33:#34 // append: (z) ljava/lang/stringBuilder;#33 = utf8 append#34 = utf8 (z) ljava/lang/stringBuilder;#35 = Methodref#24.#36 // java/lang/stringBuilder.ToString :() Ljava/lang/string;#36 = nameandtype#37 // 38 /////3 ////1 toString :() ljava/lang/string;#37 = utf8 toString#38 = utf8 () ljava/lang/string;#39 = Methodref#40.#42 // java/io/printstream.println: (lJava/lang/string;) V#40 = class#41 // java/io/iO/iO java/io/printstream#42 = nameandtype#43:#30 // println: (ljava/lang/string;) v#43 = utf8 println#44 = class#45 // java/lang/string#45 = utf8 java/lang/string#46 = Methodref#44.#29 ////// java/lang/String."<init>":(Ljava/lang/String;)V#47 = Utf8 args#48 = Utf8 [Ljava/lang/String;#49 = Utf8 s1#50 = Utf8 Ljava/lang/String;#51 = Utf8 s2#52 = Utf8 s3#53 = Utf8 s4#54 = Utf8 StackMaptable#55 = Clase#48 // "[ljava/lang/string;"#56 = UTF8 SourceFile#57 = UTF8 StringTest.Java ............ // La instrucción Bytecode para el método correspondiente es explicado y ejecutado por el tiempo de ejecución JVM. Public static void main (java.lang.string []); Descriptor: ([ljava/lang/string;) v Flags: Acc_public, Acc_static Code: stack = 4, locals = 5, args_size = 1 0: ldc #16 // string i amo a China, esta directive se refiere al símbolo en el #16 del grupo constante, y aquí empuja el símbolo de la cadena "ilove China" a la parte superior del stack. Esta instrucción corresponde a la siguiente instrucción 2. String S1 = "I Love China" Declaración 2 En el programa: Store_1 // Asigna la referencia de objeto en la parte superior de la pila a la variable local 1. 3: LDC #16 // String Amo a China, la instrucción en el mismo 0 apunta a una constante en la misma referencia del símbolo. Esta instrucción y la siguiente instrucción 5 corresponden a la declaración S2 = "I Love China" en el programa. 5: store_2 // asigne referencias de objeto a variables locales en la parte superior de la pila 2. 6: getstatic #18 // campo java/lang/system.out: ljava/io/printstream; 9: nuevo #24 // clase java/lang/stringBuilder 12: dup 13: ldc #26 // string Resultado: 15: Invokespecial #28 // método java/lang/stringBuilder. "<Init>" :( ljava/lang/string;) v 18: aload_1 19: aload_2: if_acmpne 27 // show up up up;) v 18: aload_1 19: Aload_2 20: if_acmpne 27 // Compare si son iguales o no, vaya a la Directiva 27, ejecute, ejecute la siguiente instrucción igualmente 23: iconst_1 24: goto 28 27: iconst_0 28: invokevirtual #31 // método java/lang/stringBuilder.append: (z) ljava/lang/stringBuilder; 31: InvokeVirtual #35 // método java/lang/stringBuilder.ToString :() ljava/lang/string; 34: InvokeVirtual #39 // método java/io/printstream.println: (ljava/lang/string;) v 37: nuevo #44 // class java/lang/string, crea un objeto, que se encuentra en la referencia del grupo constante #44, aquí está el objeto String, y la referencia del objeto se empuja hacia la parte superior del stack. 40: DUP // Copia una copia del objeto en la parte superior de la pila y empújala a la parte superior de la pila. 41: LDC #16 // String I Love China, igual que 0, 3 instrucciones. 43: Invokespecial #46 // método java/lang/string. "<Init>" :( ljava/lang/string;) v 46: store_3 47: nuevo #44 // class java/lang/string // crea un objeto y presione el objeto de la parte superior de la pila 50: dup 51: ldc #16 // string i love china, presione el symbol de la cita de la pila. 53: Invokespecial #46 // método java/lang/string. "<Init>" :( ljava/lang/string;) v, llame al método de inicialización de init del objeto de acuerdo con la referencia del objeto correspondiente en la parte superior de la referencia de stack y la referencia de cadena, inicialice el objeto de cadena 56: almacenar 4 // Asignar la referencia del objeto a la variable 4. 58: getstatic #18 // campo de campo, inicialice java/lang/system.out: ljava/io/printstream; 61: nuevo #24 // clase java/lang/stringBuilder 64: dup 65: ldc #26 // string resultado: 67: invokespecial #28 // método java/lang/stringBuilder.append: (z) ljava/lang/stringBuilder; 84: InvokeVirtual #35 // método java/lang/stringBuilder.ToString :() ljava/lang/stringBuilder.append: (z) ljava/lang/stringBuilder; 84: InvokeVirtual #35 // método java/lang/stringBuilder.ToString :() ljava/lang/stringBuilder; 87: InvokeVirtual #39 // método java/io/printstream.println: (ljava/lang/string;) v 90: return ......... LinenumberTable: línea 7: 0line 8: 3line 9: 6line 11: 37line 12: 47line 13: 58line 14: 90localvariaBletable: inicio longitud longitud lot firma 91 11 [Ljava/lang/string; // Variable local 088 1 S1 ljava/lang/string; // Variable local 185 2 S2 ljava/lang/string; // Variable local 244 3 S3 ljava/lang/string; // Variable local 333 4 S4 ljava/lang/string; // Variable local 4
La parte roja del bytecode está relacionada con nuestra discusión. A través del bytecode generado, podemos sacar las siguientes conclusiones para el programa de ejemplo.
1). Cuando el compilador de Java compila el programa en bytecode, la constante de cadena "I Love China" encontró primero determina si existe en el grupo constante de Bytecode. Si no existe, no lo creará. Es decir, una cadena igual, solo se puede conservar una copia. Se puede encontrar a través de referencias simbólicas, de modo que las variables de cadena S1 y S2 en el programa apuntan a la misma cadena constante en el grupo constante. En tiempo de ejecución, JVM almacenará constantes de cadena en el grupo constante de bytecode en la ubicación del área de método comúnmente llamada grupo constante, y se accede a la cadena a través de índices en forma de una matriz de caracteres. JVM señala la dirección de referencia relativa de la cadena apuntada por S1 y S2 a la dirección de memoria real de la cadena en tiempo de ejecución.
2). Para String S3 = New String ("I Love China"), String S4 = New String ("I Love China"), desde el Bytecode, se puede ver que llama la nueva instrucción. JVM creará dos objetos diferentes en tiempo de ejecución, y S3 y S4 apuntan a diferentes direcciones de objetos. Por lo tanto, el resultado de la comparación S3 == S4 es falso.
En segundo lugar, para la inicialización de los objetos S3 y S4, se ve desde el código de bytecodo que se llama el método init del objeto y se pasa la referencia "Amo a China" en el grupo constante. Entonces, ¿cuál es la creación del objeto de cadena y la inicialización? Podemos verificar el código fuente de la cadena y el bytecode generado por el objeto de cadena para comprender mejor si la cadena se copia dentro del objeto o la referencia directamente a la dirección del grupo constante correspondiente a la cadena está apuntando.
3. Parte del código fuente del objeto de cadena:
<Span style = "font-size: 14pt"> public Final Class String implementa java.io.serializable, comparable <String>, CharSequence { /** El valor se usa para el almacenamiento de caracteres. */ Valor de char Final privado []; / ** Caché el código hash para la cadena*/ private int hash; // predeterminado a 0 public string () {this.Value = new Char [0]; } </span> <span style = "fondo de fondo: #ffffff; font-size: 18pt"> public string (String original) {this.value = original.value; this.hash = original.hash; } </span>Desde el código fuente, vemos que hay un valor de caracteres variable de instancia [] en la clase de cadena. A través del método de construcción, podemos ver que el objeto no realiza operaciones de copia al inicializar, pero solo asigna la referencia de dirección del objeto de cadena pasada al valor de la variable de instancia. A partir de esto, inicialmente podemos concluir que incluso cuando se crea un objeto de cadena usando una nueva cadena ("ABC"), el espacio se asigna para el objeto en el montón de memoria, pero no se almacena información sobre "ABC" en el montón, pero la referencia a la cadena "ABC" se inicializa dentro de su variable de instancia a la cadena "ABC". De hecho, esto también es para guardar el espacio de almacenamiento de memoria y mejorar el rendimiento del programa.
4. Echemos un vistazo a la información de Bytecode del objeto de cadena:
público java.lang.string (); Descriptor: () v Flags: ACC_Public Code: stack = 2, locals = 1, args_size = 1 0: aload_0 1: invokespecial #1 // método java/lang/object. "<Init>" :() V 4: Aload_0 5: iconst_0 6: NewAray Char 8: Putfield #2 // Valor de campo: [C 11 C11 138: 4 Línea 139: 11 Java.Lang.String (java.lang.string); Descriptor: (ljava/lang/string;) v Flags: ACC_Public Code: stack = 2, locals = 2, args_size = 2 0: aload_0 // presiona la variable local 0 a la parte superior de la pila, una referencia a su propio objeto. 1: Invokespecial #1 // Método Java/Lang/Object. "<Init>" :() V Pase el objeto superior de la pila para referirse al método de inicialización en #1 del objeto. 4: Aload_0 // Empuje la referencia de su propio objeto a la parte superior de la pila. 5: Aload_1 // El empuje de referencia de cadena pasada a la parte superior de la pila. 6: Getfield #2 // Valor de campo: [c // Papá la referencia de cadena en la parte superior de la pila y asigna a la variable de instancia en #2, y guárdela en la pila. 9: Putfield #2 // Valor de campo: [c // Pop echa la referencia de cadena en la parte superior de la pila y el objeto en sí y asigne la referencia de cadena a la variable de instancia del objeto en sí. 12: Aload_0 13: Aload_1 14: Getfield #3 // Campo Hash: I 17: Putfield #3 // Campo Hash: I 20: Regreso
Desde la perspectiva de Bytecode, podemos concluir que la nueva cadena ("ABC") realiza tareas de referencias de cadenas al construir un nuevo objeto, en lugar de copiar cadenas. Lo anterior es un análisis y resumen de la asignación de memoria de las cadenas desde la perspectiva del código fuente y el código de byte.
El análisis anterior y el resumen de la asignación de memoria de cadena Java (recomendado) es todo el contenido que comparto con usted. Espero que pueda darle una referencia y espero que pueda apoyar más a Wulin.com.