Strings = newString ("xyz") crea varias instancias

Hablando de las preguntas de la entrevista

String s = new String("xyz"); 创建了几个实例?

Esta es una pregunta clásica de entrevista. En un libro llamado Java, la "respuesta estándar" que vi es esta:

两个,一个堆区的“xyz”,一个栈区指向“xyz”的s。

Esta llamada "respuesta estándar" tiene demasiados espacios, la analizaremos lentamente más adelante.

Aunque la respuesta es indignante, no creo que la pregunta en sí tenga ningún significado, porque la pregunta no define el significado específico de "crear" y especifica el tiempo de "crear". ¿Es tiempo de ejecución? ¿Cuando el paquete no incluye carga de clases? ¿Existe un contexto de código contextual? Tampoco hay una definición de a qué instancia se refiere, ¿es una instancia de Java? ¿O simplemente consulte las instancias de String? ¿El paquete no incluye instancias de C ++ en la JVM?

Evidentemente, este problema es un "problema problemático". Esta respuesta también es una "respuesta cuestionable".

Estructura de cadena

Antes del análisis, para facilitar el dibujo posterior del gráfico de memoria, necesitamos tener una comprensión general de la estructura de String en Java:

Strings = newString ("xyz") crea varias instancias

Como se puede ver en la figura anterior, la clase String tiene tres atributos:

  • valor: matriz de caracteres, utilizada para almacenar caracteres.
  • hash: el código hash de la cadena en caché, el valor predeterminado es 0 (el valor hash de la cadena se calcula cuando se llama al método hashCode).
  • serialVersionUID: utilizado para la serialización.

Problemas normales y explicaciones razonables.

Agregue algunos calificadores a la raíz anterior para obtener una nueva pregunta:

String s = new String("xyz");创建几个String实例?

Para esta pregunta, se pueden encontrar algunas respuestas muy elogiadas en Internet:

两个。
一个是字符串字面量"xyz"所对应的、存在于全局共享的常量池中的实例,
另一个是通过new String(String)创建并初始化的、内容(字符)与"xyz"相同的实例。
考虑到如果常量池中如果有这个字符串,就只会创建一个。同时在栈区还会有一个对new出来的String实例的s。

Teniendo en cuenta la pila y la pila, y mencionando la reserva constante, creo que esto ha alcanzado las expectativas de la mayoría de los entrevistadores para la respuesta a esta pregunta, y tal vez este sea también el punto que los entrevistadores quieren investigar.

Pero esta respuesta es solo razonable y no completamente correcta.

En primer lugar, lo que no entiendo es por qué muchos encuestados siempre usan "grupo constante" en lugar de "grupo constante de cadena". En el sistema Java, en realidad hay tres grupos constantes. Los conceptos y la utilidad de los tres grupos constantes son No es lo mismo, mezclados pueden causar fácilmente malentendidos a los demás.

En segundo lugar, incluso si el "grupo de constantes" mencionado por el respondedor es el "grupo de constantes de cadena", el "grupo de constantes de cadena" almacena referencias a instancias de cadena en lugar de cadenas, lo que hace una gran diferencia. Y esta respuesta no considera el entorno en el que se ejecuta el código.

Estos problemas se analizarán uno por uno a continuación.

Distinguir variables e instancias

Volvamos a la pregunta al principio y a la "respuesta estándar":

问题:String s = new String("xyz"); 创建了几个实例?
答案:两个,一个堆区的“xyz”,一个栈区指向“xyz”的s

Obviamente, la persona que escribió la respuesta no distinguió entre variables y ejemplos. En Java, una variable es una variable y una variable de un tipo es solo para una instancia de objeto o nula, no la instancia en sí. El número de variables declaradas no está necesariamente relacionado con el número de instancias creadas.

por ejemplo:

String s1 = "xyz";  
String s2 = s1.concat("");  
String s3 = null;  
new String(s1);  

Este código involucrará 3 variables de tipo String:

  • s1, apunta a 1 de la instancia de String a continuación
  • s2, apuntando igual que s1
  • s3, el valor es nulo, no apunta a ninguna instancia

Y 3 instancias de String:

  • Instancia de cadena de la constante de cadena residente correspondiente al literal "xyz"
  • La instancia de String de la constante de string residente correspondiente al literal ""
  • Una nueva instancia de String creada por new String (String), sin ninguna variable que apunte a ella

Carga de clases

Para String s = new String ("xyz"); ¿Cuántas instancias de String se crean? este problema.

Parece que todas las respuestas en Internet analizan el proceso de carga de clases y el proceso de ejecución real juntos.

Parece que no hay problema, porque para ejecutar un determinado fragmento de código, se debe cargar la clase en la que se encuentra, y para el mismo cargador de clases, se carga como máximo una vez.

Pero echemos un vistazo al código de bytes de este código:

Strings = newString ("xyz") crea varias instancias

Parece que el nuevo java / lang / String aparece solo una vez, es decir, solo se crea una instancia de String. En otras palabras, el código de la pregunta original solo creará una nueva instancia de String cada vez que se ejecute. La instrucción ldc aquí simplemente empuja una referencia de una instancia de String ("xyz") que se ha creado en el proceso de carga de clases a la parte superior de la pila de operandos, y no crea una nueva instancia de String.

¿No debería haber dos instancias? ¿Cuándo se creó otra instancia de String?

Todos sabemos que la fase de análisis de la carga de clases es un proceso en el que la máquina virtual Java reemplaza las referencias simbólicas en el grupo constante con referencias directas. Según la especificación de JVM, una implementación de JVM compatible debe crear y residir una instancia de String como constante durante el proceso de carga de clases. Corresponde al literal "xyz", específicamente en la etapa de análisis de carga de clases. Esta constante se comparte globalmente, y es necesario crear una nueva instancia de String solo si no ha residido antes ninguna cadena con el mismo contenido.

Entonces, puede comprender que en la etapa de análisis de la carga de clases, se ha creado una instancia de String y, cuando se ejecuta el código, se crea una instancia de String. Por supuesto, no hay problema si junta los dos para discutir.

Optimización de JVM

La discusión anterior es solo para el lenguaje Java y la máquina virtual Java definidos por la especificación. Este es conceptualmente el caso, pero la implementación real de la JVM puede ser más optimizada. El fragmento de código en la pregunta original puede no ser creado completamente cuando se ejecuta una instancia de String (sin espacio asignado).

Decir que es la "respuesta estándar" sin considerar el código de contexto es una pifia.

Veamos este código:

Strings = newString ("xyz") crea varias instancias

La ejecución de este código continuará creando objetos String para comer memoria y, a continuación, provocará GC.

Creo que todos no tienen opinión sobre esta conclusión, agregamos -XX: + PrintGC -XX: -DoEscapeAnalysis para imprimir el registro y desactivar el análisis de escape (JDK8 activa esta optimización de forma predeterminada, la desactivaremos primero)

Strings = newString ("xyz") crea varias instancias

Ejecútelo para ver:

Strings = newString ("xyz") crea varias instancias

El resultado es, de hecho, el que esperábamos, creando constantemente objetos String y comiendo memoria que conduce a GC frecuente.

Ahora cambiaremos -XX: -DoEscapeAnalysis a -XX: + DoEscapeAnalysis y ejecutaremos este código nuevamente:

Strings = newString ("xyz") crea varias instancias

Sucedió algo mágico, seguí corriendo y no salieron más registros de GC. ¿El objeto String recién creado ya no consume memoria?

La situación real es: Después de la optimización de HotSpot VM, el método newString () no creará una nueva instancia de String. De esta manera, la memoria no se consume naturalmente y la GC ya no se activa.

Ahora veamos la pregunta al principio. Sin combinar las circunstancias específicas, ¿podemos simplemente decir que String s = new String ("xyz"); creará dos instancias de String?

Acabo de dar un ejemplo de análisis de escape: HotSpot VM tiene muchas optimizaciones como esta, como la inserción de métodos, la sustitución escalar y la eliminación de código inútil.

clase abierta

Si no hay ningún atributo de la instancia "Java" agregado al tema, entonces no debemos ignorar la instancia oop en JVM.

Para explicar mejor esto más adelante, necesitamos agregar conocimiento del modelo klass-opp. Primero haga un acuerdo, siempre que el contenido involucrado en la implementación específica de JVM esté basado en HotSpot VM en Jdk8.

HotSpot VM se implementa en base a C ++, y C ++ es un lenguaje orientado a objetos, que en sí mismo tiene las características básicas de orientado a objetos, por lo que la forma más sencilla de representar objetos en Java es generar una clase C ++ correspondiente a cada clase Java. Pero HotSpot VM no hizo esto, sino que diseñó un conjunto de modelos klass-oop.

klass, es la forma de existencia de la metainformación de las clases Java en la JVM. Una vez que el cargador de clases JVM carga una clase Java, existe en la JVM en forma de klass.

Strings = newString ("xyz") crea varias instancias

oop, es la forma de existencia de objetos Java en la JVM. Cada vez que se crea un nuevo objeto, se crea un objeto OOP del tipo correspondiente en la JVM.

Entre ellos, instanceOopDesc ​​representa un objeto que no es una matriz y arrayOopDesc ​​representa un objeto de matriz;

Y objArrayOopDesc ​​representa un objeto de matriz de tipo de referencia, y typeArrayOopDesc ​​representa un objeto de matriz de tipo básico.

Por ejemplo: una instancia de la clase String en Java, habrá una instancia correspondiente instanceOopDesc ​​en la JVM.

Strings = newString ("xyz") crea varias instancias

Grupo constante de cadenas

En el sistema Java, hay tres grupos constantes:

  • Pool constante en bytecode de clase: existe en el disco duro. Existen principalmente dos tipos de constantes: literales y referencias simbólicas.
  • Pool de constantes de tiempo de ejecución: parte del área de métodos. El grupo de constantes que solemos decir se refiere a esta área: el grupo de constantes de tiempo de ejecución en el área de método.
  • Pool de constantes de cadena: existe en el área del montón. Este grupo de constantes es una StringTable en el nivel de JVM, que solo almacena referencias a instancias java.lang.String y no almacena el contenido de los objetos String. Generalmente, cuando decimos que una cadena ingresa al grupo de constantes de cadena, significa que una referencia a ella se guarda en esta StringTable. Por el contrario, si no está en ella, significa que no hay ninguna referencia a ella en la StringTable. .

Hoy, lo que queremos entender es el grupo constante de cadenas.

Pool de constantes de cadena, a saber, String Pool. La clase correspondiente en JVM es StringTable y la implementación subyacente es Hashtable. Se utiliza la idea de hash.

Strings = newString ("xyz") crea varias instancias

El siguiente fragmento de código es agregar un método de cadena al grupo de constantes de cadena. Aunque es código C ++, creo que cualquiera que haya estudiado Java puede entenderlo, o al menos entender lo que hace este código. El índice de subíndice se ubica mediante el valor hash generado por el contenido de la cadena + la longitud, y luego el instanceOopDesc ​​correspondiente a la instancia de la clase Java String se encapsula en un HashtableEntry como una estructura de almacenamiento y se almacena en el grupo constante.

Strings = newString ("xyz") crea varias instancias

Después de complementar el conocimiento del grupo de constantes de cadena, volvemos a la pregunta al comienzo del artículo:

String s = new String ("xyz"); ¿Cuántas instancias se han creado?

Dibujamos un diagrama de memoria, la figura omite dos instancias de instanceOopDesc ​​correspondientes a String.

Strings = newString ("xyz") crea varias instancias

No es difícil obtener la respuesta:

如果包括JVM中的C++实例的话,
有两个Java的String实例,
两个String实例对应的instanceOopDesc实例,
还有一个char[]数组对应的typeArrayOopDesc实例。
加一起一共是5个,也可以说2个String实例加上3个oop实例。

para resumir

String s = new String ("xyz"); ¿Cuántas instancias se han creado?

A través del análisis anterior, encontraremos que cada vez que se agrega un atributo a la raíz de este tema, el tema tendrá una respuesta diferente.

Ya sea para considerar el proceso de carga de clases, si considerar la optimización de JVM, si incluir la instancia de oop correspondiente, etc., vale la pena hablar de cada punto.

La próxima vez que alguien te pregunte, también podrías compartir este artículo con él.

Supongo que te gusta

Origin blog.csdn.net/doubututou/article/details/111865890
Recomendado
Clasificación