Hablando de JVM (entrevista a menudo prueba)

Introducción a JVM

JVM es la abreviatura de Java Virtual Machine, que significa Java Virtual Machine.
Una máquina virtual se refiere a un sistema informático completo con funciones de hardware completas simuladas por software y que se ejecuta en un entorno completamente aislado.

Máquinas virtuales comunes: JVM, VMwave, Virtual Box.
La diferencia entre la JVM y las otras dos máquinas virtuales:

  1. VMwave y VirtualBox son conjuntos de instrucciones que simulan CPU físicas a través de software y hay muchos registros en el sistema físico;

  2. La JVM simula el conjunto de instrucciones de los bytecodes de Java a través del software. En la JVM, solo se reserva principalmente el registro de la PC y se recortan otros registros .
    La JVM es una computadora personalizada que no existe en la realidad.

En el desarrollo diario, los programadores de Java generalmente no usan cosas dentro de la JVM. Si desea comprender más profundamente, puede leer este libro, hay muchos productos secos
inserte la descripción de la imagen aquí

1. División del área de memoria de JVM

inserte la descripción de la imagen aquí

La memoria JVM se aplica desde el sistema operativo, se divide en diferentes áreas y diferentes áreas completan diferentes funciones

什么是线程私有?
Dado que los subprocesos múltiples de la JVM se logran cambiando los subprocesos a su vez y asignando el tiempo de ejecución del procesador, en un momento dado, un procesador (un procesador de varios núcleos se refiere a un núcleo) solo ejecutará una instrucción de subproceso. Por lo tanto, para restaurar la posición de ejecución correcta después de cambiar los subprocesos, cada subproceso necesita un contador de programa independiente, y los contadores entre cada subproceso no se afectan entre sí y se almacenan de forma independiente. A este tipo de área la llamamos memoria "privada por subprocesos".

1.1 Contador de programa (subproceso privado)

La función del contador de programa: se utiliza para registrar el número de línea ejecutado por el hilo actual.

Está en la memoria, donde está la dirección 最小的区域del siguiente elemento a ejecutar 指令...

指令Es el código de bytes. Si el programa quiere ejecutarse, la JVM tiene que cargar el código de bytes y ponerlo en la memoria. , cuál se está ejecutando actualmente. La
CPU es un proceso de ejecución concurrente, no solo proporciona servicios para un proceso , tiene que servir a todos los procesos, precisamente porque el sistema operativo se programa y ejecuta en unidades de hilos, cada hilo tiene que registrar su propia ubicación de ejecución, es decir, el contador del programa, uno para cada hilo.

1.2 Pila de máquina virtual Java (subproceso privado)

Describe las variables locales y la información de la llamada al método. Cuando se llama a un método, cada vez que se llama a un método nuevo, implica una operación "push", y cada vez que se ejecuta un método, implica una operación "pop"
. es relativamente pequeño, el tamaño del espacio de la pila se puede configurar en la JVM, pero generalmente son solo unos pocos M o decenas de M, por lo que es muy probable que la pila esté llena (normalmente, generalmente estamos bien al escribir código, pero tenemos miedo de la recursividad Configure, habrá un desbordamiento de pila: StackOverflowException)

El rol de la pila de la máquina virtual Java: El ciclo de vida de la pila de la máquina virtual Java es el mismo que el del subproceso.La pila de la máquina virtual Java describe que
内存模型:cada método ejecutado por un método Java creará un marco de pila (Stack Frame) para el almacenamiento 局部变量表、操作数栈、动态链接、方法出口y otra información en el mismo momento de la ejecución. En la memoria de pila y la memoria de pila de las que solemos hablar, la memoria de pila se refiere a la pila de la máquina virtual.
La pila de la máquina virtual Java incluye las siguientes cuatro partes:
inserte la descripción de la imagen aquí1. Tabla de variables locales: almacena varios tipos de datos básicos (8 tipos de datos básicos) y referencias de objetos conocidas por el compilador. El espacio de memoria requerido por la tabla de variables locales se asigna durante la compilación. Al ingresar un método, se determina completamente cuánto espacio de variables locales necesita el método para asignar en el marco, y el tamaño de la tabla de variables locales no cambiará durante la ejecución. En pocas palabras, almacena parámetros de métodos y variables locales.
2. Pila de operaciones: cada método genera una pila de operaciones de tipo primero en entrar, último en salir.
3. Vinculación dinámica: una referencia de método al conjunto de constantes de tiempo de ejecución.
4. Dirección de retorno del método: la dirección del registro de PC

1.3 Pila de métodos nativos (subproceso privado)

La pila de métodos nativos es similar a la pila de máquinas virtuales, excepto que la JVM usa la pila de máquinas virtuales Java y el método nativo usa la pila de métodos nativos.

1.4 Montón (hilo compartido)

El papel del montón: todos los objetos creados en el programa se almacenan en el montón

Un proceso tiene solo una copia, y varios subprocesos comparten un montón, que también es el área con el mayor espacio en la memoria. El nuevo objeto que creamos está en el montón, y las variables miembro del objeto, naturalmente, también están en el montón.

Nota: 内置类型的变量在栈上,引用类型的变量在堆上esta declaración es incorrecta,
debería ser que las variables locales están en la pila, las variables miembro y los nuevos objetos están en el montón
inserte la descripción de la imagen aquí

1.5 Área de método (hilo compartido)

La función del área de método: se utiliza para almacenar datos como información de clase, constantes, variables estáticas y código compilado por el compilador en tiempo real cargado por la máquina virtual.

En el área de métodos, se coloca el "objeto de clase", el llamado "objeto de clase": .javael código que escribimos se convertirá en .class(código de bytes binario) y .classse cargará en la memoria, es decir, la clase construida por el Objeto JVM. (el proceso de carga se llama "carga de clase"), "objeto de clase" describe cómo se ve la clase, cuál es el nombre de la clase, qué miembros hay en ella, qué métodos hay y cuál es el nombre de cada miembro ?Tipo (público/privado...), cómo se llama cada método, de qué tipo es (público/privado...), las instrucciones que contiene el método...
Hay otra cosa muy importante en "clase objeto", 静态成员(estático)

Los miembros modificados por estática se convierten en "atributos de clase", mientras que los miembros ordinarios se denominan "atributos de instancia".

2. Mecanismo de carga de clase JVM

La carga de clases es en realidad una función central importante del diseño de un entorno de tiempo de ejecución. ¿Qué hace la carga de clases? Carga .classarchivos en la memoria y los convierte en objetos de clase.

2.1 Proceso de carga de clases

Ciclo de vida de carga de clases:
inserte la descripción de la imagen aquíLos primeros 5 pasos están en un orden fijo y también son el proceso de carga de clases. Los 3 pasos intermedios están todos conectados, por lo que para la carga de clases, se divide en tres pasos: Loading,Linking,Initialization(Cuando responda a otros, intente usar ingles)

2.1.1 Carga

La etapa de "Carga" es una etapa en todo el proceso de "Carga de clases". Es diferente de la Carga de clases. Una es Carga y la otra es Carga de clases, así que no las pongas juntas.

在加载 Loading 阶段,Java虚拟机需要完成以下三件事情:
1) Obtenga el flujo de bytes binarios que define esta clase por su nombre completo.
2) Convierta la estructura de almacenamiento estática representada por este flujo de bytes en la estructura de datos de tiempo de ejecución del área de método.
3) Genere un objeto java.lang.Class que represente esta clase en la memoria como la entrada de acceso para varios datos de esta clase en el área de métodos.

El resumen es primero encontrar el .classarchivo correspondiente, luego abrir y leer el .classarchivo (flujo de bytes) e inicialmente generar un objeto de clase

En un enlace clave en Cargando, .class¿qué hay exactamente en el archivo? Para obtener más información
inserte la descripción de la imagen aquí, consulte la documentación oficial: https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html

De acuerdo con el formato de la figura anterior, la información leída y analizada se completará inicialmente en el objeto de clase

2.1.2 Vinculación

Una conexión es generalmente el establecimiento de una conexión entre múltiples entidades.

一: 验证(Verification)
Lo principal es verificar si el contenido leído coincide exactamente con el formato especificado en la especificación.Si se encuentra que el formato de datos leído aquí no se ajusta a la especificación, la carga de la clase fallará y se lanzará una excepción.

二: 准备(Preparation)
La etapa de preparación consiste en asignar formalmente memoria para las variables definidas en la clase (es decir, variables estáticas, variables modificadas por estáticas) y establecer el valor inicial de la variable de clase
比如:
.

三: 解析(Resolution)
La fase de análisis es el proceso en el que la máquina virtual Java reemplaza las referencias simbólicas en el conjunto de constantes con referencias directas, es decir, el proceso de inicialización de constantes.
Significa que en el .classarchivo , las constantes están ubicadas en el centro, y cada constante tiene un número. .classLa situación inicial en la estructura del archivo solo registra el número, por lo que es necesario encontrar el contenido correspondiente de acuerdo con el número y completarlo en el objeto de clase.

2.1.3 Inicialización

En la fase de inicialización, la máquina virtual Java realmente comienza a ejecutar el código del programa Java escrito en la clase y transfiere el dominio a la aplicación. La fase de inicialización es el proceso de ejecutar el método constructor de clases, que consiste en inicializar realmente el objeto de la clase, especialmente para los miembros estáticos.

Preguntas típicas de la entrevista: ¿Cuándo se activará la carga de una determinada clase (ejemplo de código)?
inserte la descripción de la imagen aquí
¿Cuál es su orden de impresión?
Resultado:
inserte la descripción de la imagen aquí
siempre que se use la clase, esta debe cargarse primero (como creación de instancias, métodos de llamada, llamada estática métodos, heredados... todos contados como usados)

大的原则:
1: El bloque de código estático se ejecutará en la fase de carga de clases. Para crear una instancia, es necesario realizar primero la carga de clases;
2: El bloque de código estático solo se ejecutará una vez en la fase de carga de clases
. Ejecución, el código de construcción el bloque está delante del método de construcción
4: la clase principal se ejecuta primero y la subclase se ejecuta en la parte posterior
5: nuestro programa se ejecuta desde el principal, principal aquí está el método de prueba, por lo que para ejecutar el principal, debe necesita cargar TestDemo primero

Nuestro programa se ejecuta desde el método principal, principal aquí es el método de la clase TestDemo, por lo que para ejecutar principal primero, debe cargar TestDemo primero, y TestDemo hereda B, para cargar TestDemo, debe cargar B primero y B hereda A, y carga A primero

2.2 Modelo de delegación de padres

Esta cosa no es muy útil en nuestro trabajo, pero a menudo se pregunta durante las entrevistas...
Esta cosa es un enlace en la carga de clase, este enlace está en la etapa de carga (la parte frontal), el modelo de delegación de padres, de hecho, está en la JVM 类加载器, cómo encontrar el .classarchivo

类加载器:La JVM proporciona un objeto especial, llamado cargador de clases, que es responsable de la carga de clases. Por supuesto, el proceso de búsqueda de archivos también es responsabilidad del cargador de clases... Los
.classarchivos se pueden colocar en muchas ubicaciones, algunos se colocan en el directorio JDK, y algunos se colocan en el directorio del proyecto, algunos se colocan en otras ubicaciones específicas, etc. Por lo tanto, se proporcionan múltiples cargadores de clases en la JVM, y cada cargador de clases es responsable de un segmento...

Existen principalmente tres cargadores de clases predeterminados:
1: BootstrapClassLoader
responsable de cargar las clases en la biblioteca estándar (String, ArrayList, Random, Scanner...)
2: ExtensionClassLoader
responsable de cargar las clases extendidas de JDK (rara vez se usa ahora)
3: ApplicationClassLoader
responsable de cargar las clases en el directorio del proyecto actual

Además, los programadores también pueden personalizar el cargador de clases para cargar clases en otros directorios. Por ejemplo, Tomcat personaliza el cargador de clases para cargar específicamente el .class...

Nuestro modelo de delegación de padres describe este proceso de encontrar el directorio, es decir, cómo funcionan juntos los cargadores de clases anteriores...
inserte la descripción de la imagen aquí

Este conjunto de reglas de búsqueda se denomina "modelo de delegación parental" (esto se translitera, el padre puede ser el padre o la madre, de acuerdo con las reglas, no es imposible llamarlo el "modelo de delegación monoparental", por supuesto, el nombre no depende de nosotros decidir)

¿Por qué la JVM está diseñada de esta manera? ¡
La razón es que una vez que se repite el nombre de clase completamente calificado de la clase escrita por el programador y la clase en la biblioteca estándar, la clase en la biblioteca estándar también se puede cargar sin problemas
! java.lang Las clases como .String las definimos nosotros mismos, si el programa está cargado, sigue siendo una clase en la biblioteca estándar, por lo que no habrá conflictos y la seguridad está garantizada.

¿Si el cargador de clases personalizado también cumple con el modelo de delegación principal?
Se puede cumplir o no, según los requisitos.
Al igual que Tomcat carga clases en aplicaciones web, no cumple (porque el cumplimiento no tiene sentido)

3. Mecanismo de recolección de basura (GC) de JVM

3.1 ¿Qué es la recolección de basura?

Recolección de basura (GarbageCollection, GC), cuando escribimos código, a menudo solicitamos memoria, creamos variables, nuevos objetos, cargamos clases... Todo esto solicita memoria, todo desde el sistema operativo, ya que se aplica memoria, luego Definitivamente van 不用的时候a devolver la memoria también.

En términos generales, el momento de solicitar la memoria es claro (si necesita guardar algunos datos, debe solicitar la memoria), pero el período de liberación de la memoria no es tan claro. No sabemos si todavía necesitamos esta memoria. .

Por ejemplo: Supongamos que cuando llegas a casa por la tarde, simplemente tiras tu ropa y no te preocupas por ella. Cuando tu madre se entera, organiza tu ropa y la pone en el armario. Al día siguiente, si no No lo uses, está bien, pero tienes que usar este vestido para salir, vas a la ubicación original para encontrarlo, ¿eh? Se ha ido, ¿no es vergonzoso... ¿Está bien (这就是内存释放早了)
lanzarlo más tarde? no muy bien, como Ocupas un asiento en la biblioteca, ocupas el asiento temprano en la mañana, y no vas en todo el día, ¿no es vergonzoso, no necesitas ocupar el asiento, y nadie otro puede usarlo (这就是内存释放迟了)
y lo que queremos es poder no tarde o temprano

3.2 ¿Por qué aparece el mecanismo de recolección de basura?

Como el lenguaje C: "No me importa la liberación de memoria, los programadores pueden hacerlo ustedes mismos, de todos modos, no descontarán mi dinero..." Por lo tanto, en el lenguaje C, se encontrará con un dolor de cabeza común = > "内存泄露"(申请之后,忘了释放)=> ¡La memoria disponible es cada vez menor y finalmente no hay memoria disponible! Por lo tanto, la "pérdida de memoria" es un dolor de cabeza para los programadores de C/C++, algunas se filtran rápidamente, otras se filtran lentamente y el momento de la exposición es incierto, si aparece, es difícil de investigar.C++ propuso más tarde un 智能指针(大概就只是简单依赖了一下 C++ 中的 RAII机制,其实一点也不智能..)mecanismo como este, a través del cual el riesgo de "pérdida de memoria" puede reducirse hasta cierto punto... Pero es un hermano menor frente a muchos mecanismos de java (gracioso )

Entonces, como Java, GO, PHP... La mayoría de los principales lenguajes de programación en el mercado ahora han adoptado una solución, ¡que es el mecanismo de recolección de basura!
Probablemente haya un entorno de tiempo de ejecución (como JVM, intérprete de Python, Go tiempo de ejecución...) Para determinar si la memoria se puede recuperar a través de una estrategia más compleja y realizar la acción de recuperación... La recolección de elementos no utilizados, en esencia, se basa en el entorno de tiempo de ejecución y hace mucho trabajo adicional para completar la operación de liberar automáticamente la memoria, lo que supone una gran carga para la mente del programador.

Sin embargo, la recolección de basura también tiene desventajas:
1: Consume gastos generales adicionales (se consumen más recursos)
2: Puede afectar el buen funcionamiento del programa (la recolección de basura a menudo presenta el problema STW (Stop The World, como el tiempo quieto))

La recolección de basura es tan fragante, ¿por qué C ++ no introduce GC?
De hecho, algunos jefes han propuesto este plan, pero no se ha implementado, porque el lenguaje C ++ tiene dos líneas de alto voltaje, que son sus principios fundamentales:
1:es compatible con el lenguaje C, y también puede ser compatible con varios Todo tipo de sistemas operativos de hardware están maximizados y son compatibles con
2: el rendimiento más extremo...
como inteligencia artificial, motores de juegos, servidores de alto rendimiento, kernels de sistemas operativos... Para escenarios con requisitos de compatibilidad/rendimiento extremadamente altos, aún se requiere C/C++.

3.3 Qué reciclar en la recolección de basura

Lo que se reclama es memoria, pero la memoria incluye: contador de programa, pila, montón y método, algunos se reclaman, otros no se reclaman:

程序计数器:Tamaño fijo, no hay liberación involucrada, por lo
栈:que no es necesario ejecutar la función GC, y el marco de pila correspondiente se libera automáticamente, por lo que no es necesario que GC
堆:requiera GC. Una gran cantidad de memoria en el código está en el objeto de clase heap
方法区:, que es cargado por la clase. Para realizar la "descarga de clase", la memoria debe liberarse, y la operación de descarga es en realidad una operación de muy baja frecuencia (rara vez implica la recolección de basura)

Discutiremos la recolección de basura en el montón aquí.Primero
mire esta imagen:
inserte la descripción de la imagen aquí

La imagen de arriba se puede entender como tres facciones: 积极派,消极派,中间摇摆派,

积极派: La memoria que está en uso no será liberada
消极派:. La memoria que ya no está en uso debe ser liberada
中间摇摆派:. La parte entre rojo y azul representa que algunas están en uso y otras no. En este caso 是不释放的, no se libera . hasta que se agote.

No habrá "medio objeto" en GC, principalmente para hacer que la recolección de basura sea más conveniente y simple, recuerde:垃圾回收的基本单位是"对象",而不是字节

3.4 Cómo implementar la recolección de basura

se divide en dos grandes etapas,第一阶段: 找垃圾/判定垃圾.., 第二阶段: 释放垃圾..

Al igual que limpiar la habitación, primero echa toda la basura en el bote de basura y luego tírala fuera de la habitación...

3.4.1 Cómo encontrar basura/basura de juicio

Nuestro pensamiento convencional actual tiene dos soluciones:
1: 基于引用计数(不是Java中采取的方案,这是别的语言,像Python采取的方案)
2:基于可达性分析(这个是Java采取的方案)
preste atención a cuando otros le preguntan:
1: Hablar sobre cómo determinar si es basura
en el mecanismo de recolección de basura 2: Hablar sobre cómo determinar si es basura en el mecanismo de recolección de basura de Java
Estos dos problemas están enfrentados, esto se basa en el análisis de accesibilidad, pero se basa en el recuento de referencias.

① Basado en el conteo de referencia

Para cada objeto, se introducirá una pequeña porción de memoria adicional para guardar cuántas referencias a este objeto apuntan a él.

Por ejemplo: Test t = new Test(); t es una referencia a este objeto, por lo que el objeto Test tiene una referencia y el número de referencias es 1.
inserte la descripción de la imagen aquí
Si escribe de nuevo: Test t2 = t , significa que ambos t y t2 apuntan a este objeto, en cuyo punto nuestro recuento de referencia se convierte en 2
inserte la descripción de la imagen aquí

Cuando el recuento de referencias es 0, no está en uso, se considera basura y se libera la memoria
inserte la descripción de la imagen aquí

Desventajas del conteo de referencias:
1: ¡la utilización del espacio es relativamente baja! ! Cada nuevo objeto debe ser emparejado con un contador (el contador asume 4 bytes), si el objeto en sí es muy grande (cientos de bytes), 4 bytes más, no es nada, pero si el objeto en sí es pequeño (solo tengo 4 bytes), 4 bytes más, lo que equivale a la mitad del espacio desperdiciado.
2:Habrá un problema de referencia circular
inserte la descripción de la imagen aquí

② Basado en análisis de accesibilidad

Es escanear los objetos en todo el espacio de la memoria periódicamente a través de subprocesos adicionales. Hay algunas posiciones iniciales (llamadas GCRoots), que serán similares al recorrido primero en profundidad, marcando todos los objetos a los que se puede acceder. (con marcado El objeto es accesible), el objeto que no está marcado es inalcanzable, es decir, basura...

GCRoots: se refiere a variables locales en la pila, objetos a los que apuntan las referencias en el grupo de constantes, objetos a los que apuntan los miembros estáticos en el área de métodos...

por ejemplo:
inserte la descripción de la imagen aquí

优点:Supere las dos deficiencias del conteo de referencias, la baja utilización del espacio, el problema de referencia circular
缺点:, la alta sobrecarga del sistema, si hay demasiados objetos en la memoria, puede ser lento recorrer una vez, consumiendo tiempo y recursos del sistema

En resumen, el núcleo de encontrar basura es confirmar si el objeto se usará en el futuro, entonces, ¿qué no se usa?, es decir, si no hay un punto de referencia, no se usará.

3.4.2 Algoritmo de recolección de basura

① Algoritmo de barrido de marcas

Marcar es el proceso de análisis de accesibilidad, y borrar es liberar directamente la memoria
inserte la descripción de la imagen aquí
. Si la memoria se libera directamente en este momento, aunque la memoria se devuelve al sistema, encontramos que la memoria liberada es discreta y no continua, y la problema que nos trae es "Fragmentación de memoria"

Hay mucha memoria libre, si suponemos que la memoria total es 1G, si aplicamos 500M de memoria, también es posible que la aplicación falle (porque los 500M aplicados son memoria continua), y cada aplicación, la la memoria debe ser un espacio continuo, y la memoria libre de 1G aquí puede ser simplemente "fragmentación de la memoria", sumando hasta 1G

② Algoritmo de copia

Para resolver el "problema de la fragmentación de la memoria", se introdujo un algoritmo de copia. En términos generales, es "usar la mitad, perder la mitad".
inserte la descripción de la imagen aquí

¡Copie directamente lo que no es basura en la otra mitad y libere el espacio original como un todo!
inserte la descripción de la imagen aquí

优点:Resuelto el problema de la "fragmentación de la memoria"
缺点:1. Baja utilización del espacio de la memoria 2. Si hay muchos objetos para retener y pocos objetos para liberar, la sobrecarga de replicación será muy grande.

③ Algoritmo de clasificación de marcado

inserte la descripción de la imagen aquí

优点:La utilización del espacio es alta, pero el problema del alto
缺点:costo de copiar/mover elementos aún no se resuelve

Aunque todo lo anterior tiene fallas, la implementación en la JVM utilizará una combinación de varios esquemas.

④ Algoritmo de reciclaje generacional

Para clasificar los objetos (clasificados según la "edad" del objeto), un objeto ha sobrevivido a una ronda de escaneo GC, que se denomina "un año más viejo", y se adoptan diferentes planes para objetos de diferentes edades...

inserte la descripción de la imagen aquí

注意:Puede haber un dicho en Internet: el 98% de los objetos nuevos no pueden sobrevivir a una ronda de GC, y el 2% de los objetos nuevos ingresarán al área de supervivencia. Este número en realidad no es confiable. Si alguien pregunta, es mejor no decir eso. , solo diga que la mayoría de los objetos no pueden sobrevivir una ronda de GC.

Supongo que te gusta

Origin blog.csdn.net/chenbaifan/article/details/125569121
Recomendado
Clasificación