1. La importancia del mecanismo de reciclaje de basura Una característica destacada del lenguaje Java es la introducción de un mecanismo de recogida de basura, que resuelve el problema más problemático de gestión de memoria para los programadores de C++, de modo que los programadores Java ya no tienen que tener en cuenta la gestión de memoria al escribir programas. Debido al mecanismo de recogida de basura, los objetos en Java ya no tienen el concepto de "alcance", solo la referencia del objeto tiene un "alcance".La recogida de basura puede prevenir eficazmente la fuga de memoria y utilizar eficazmente la memoria inactiva.
ps:内存泄露是指该内存空间使用完毕之后未回收,在不涉及复杂数据结构的一般情况下,Java 的内存泄露表现为一个内存对象的生命周期超出了程序需要它的时间长度,我们有时也将其称为“对象游离”。
2. Algoritmos en mecanismos de recogida de basura La especificación del lenguaje Java no especifica explícitamente qué algoritmo de recogida de basura usar en la JVM, pero cualquier algoritmo de recogida de basura generalmente necesita hacer dos cosas básicas: (1) encontrar objetos de información inútiles; (2) Recuperar el espacio de memoria ocupado por objetos inútiles, para que el programa pueda volver a usar ese espacio.
1. Recaudador de Referencias 1.1 Análisis de algoritmos
El conteo de referencias es una estrategia temprana en los recogedores de basura. En este enfoque, hay un conteo de referencia para cada instancia de objeto en el heap. Cuando se crea un objeto y la instancia del objeto se asigna a una variable, el recuento de variables se establece en 1. Cuando cualquier otra variable se asigna como referencia a este objeto, el conteo se suma por 1 (a = b, entonces el contador de la instancia del objeto referenciada por b es +1), pero cuando una referencia a una instancia de objeto ha excedido su vida útil o se establece en un nuevo valor, el contador de referencia de la instancia del objeto se resta por 1. Cualquier instancia de un objeto con un contador de referencia 0 puede ser recogida como basura. Cuando una instancia de objeto es recogida por basura, el contador de referencia de cualquier instancia de objeto a la que se referencia es menos 1. 1.2 Ventajas y desventajas Mérito:
El colector de conteo de referencia puede ejecutarse muy rápidamente, entrelazado con la ejecución del programa. Es ventajosa para entornos en tiempo real donde los programas no necesitan ser interrumpidos durante largos periodos de tiempo. Defecto:
No se pueden detectar referencias circulares. *Si el objeto padre tiene una referencia a un objeto hijo, el objeto hijo a su vez hace referencia al objeto padre. De este modo, su recuento de citas nunca puede ser 0. 1.3 El algoritmo de conteo de referencias no puede resolver el problema de referencia circular, por ejemplo:
/** * Java学习交流QQ群:589809992 我们一起学Java! */public class Main { public static void main(String[] args) { MyObject object1 = new MyObject(); MyObject object2 = new MyObject(); object1.object = object2; object2.object = object1; object1 = null; object2 = null; }} Las dos últimas frases asignan a object1 y object2 a null, lo que significa que los objetos a los que apuntan objetos1 y objeto2 ya no pueden ser accedidos, pero como se refieren entre sí, sus contadores de referencia no son 0, por lo que el recogedor de basura nunca los reciclará.
2. Colector de trazado o algoritmo de marcado y barrido 2.1 Algoritmo de búsqueda raíz
El algoritmo de búsqueda raíz se introduce a partir de la teoría de grafos en matemáticas discretas; el programa considera todas las relaciones de referencia como un grafo, comenzando desde un nodo GC ROOT, buscando el nodo de referencia correspondiente, tras encontrar ese nodo, continuando buscando el nodo de referencia de este nodo, y cuando se buscan todos los nodos de referencia, los nodos restantes se consideran nodos no referenciados, es decir, nodos inútiles. Java que puede usarse como raíz de GC 1. Objetos referenciados en la pila de máquinas virtuales (tabla de variables locales) 2. El objeto referenciado por el atributo estático en el área del método 3. El objeto referenciado por la constante en el área del método 4. Objetos referenciados en la pila de métodos local (objetos nativos)
2.2 Diagrama esquemático del algoritmo de trazado
![]()
2.3 Análisis del algoritmo de eliminación de marcadores
El algoritmo de purga de etiquetas escanea desde la colección raíz, marca los objetos supervivientes y luego escanea todo el espacio en busca de objetos no etiquetados para reciclar, como se muestra en la figura anterior. El algoritmo de purga de etiquetas no necesita mover objetos, y solo procesa objetos que no sobreviven, lo cual es extremadamente eficiente cuando hay muchos objetos supervivientes, pero dado que el algoritmo de purga de etiquetas recicla directamente los objetos que no sobreviven, causará fragmentación de la memoria.
3. Algoritmo de compactación o algoritmo de acabado de etiquetas
![]()
El algoritmo de etiqueta-terminado utiliza el mismo método que el algoritmo de limpieza de etiquetas para etiquetar objetos, pero es diferente al purgar; tras recuperar el espacio ocupado por objetos no supervivientes, traslada todos los objetos supervivientes al espacio libre en el extremo izquierdo y actualiza el puntero correspondiente. El algoritmo de terminación de etiquetas se basa en el algoritmo de purga de etiquetas y mueve objetos, por lo que es más caro, pero resuelve el problema de la fragmentación de la memoria. En la implementación de colectores basados en el algoritmo de compactación, generalmente se añaden las tablas de manillas y asas.
4. Algoritmo de copia (colector de compactación)
![]()
El algoritmo se propone para superar la sobrecarga del mango y resolver la recogida de basura de los restos del montón. Cuando el objeto está lleno, la recogida de basura basada en el algoritmo de copia escanea el objeto activo desde el conjunto raíz y copia cada objeto activo a la cara libre (de modo que no haya agujeros libres entre la memoria ocupada por el objeto activo), de modo que la superficie libre se convierte en la cara del objeto, la cara original del objeto la cara libre, y el programa asigna memoria en la nueva cara del objeto. Un recogidor de basura típico basado en el algoritmo de coping es el algoritmo de parar y copiar, que divide el montón en caras de objeto y caras de área libre, y el programa pausa la ejecución durante el cambio entre caras de objeto y caras de área libre.
5. Coleccionista generacional
![]()
La estrategia generacional de reciclaje de basura se basa en el hecho de queEl ciclo de vida de distintos objetos es diferente。 Por lo tanto, objetos con diferentes ciclos de vida pueden adoptar distintos algoritmos de reciclaje para mejorar la eficiencia del reciclaje.
Generación joven 1. Todos los objetos recién generados se colocan primero en la generación más joven. El objetivo de la generación más joven es coleccionar esos objetos con un ciclo de vida corto lo más rápido posible.
2. La memoria de la nueva generación se divide en una región del Edén y dos zonas supervivientes (superviviente0, superviviente1) según la proporción de 8:1:1. Una zona Edén, dos zonas de Supervivientes (en general). La mayoría de los objetos aparecen en la zona del Edén. Cuando el área de superviviente también está llena, el área del Edén y el área del superviviente 0 se copian a otra área de superviviente1, y luego el área del Edén y el superviviente0 se vacian, y después el área del superviviente0 queda vacía, y después el área del superviviente y el área del superviviente1 se intercambian. Es decir, mantener el área de superviviente vacía, y así sucesivamente.
3. Cuando el área superviviente 1 no es suficiente para almacenar los objetos supervivientes del Edén y Superviviente0, los objetos supervivientes se almacenan directamente en la era antigua. Si la vejez también está llena, se activará una GC completa, es decir, la nueva generación y la generación anterior serán recicladas
4. El GC de la nueva generación también se llama GC Menor, y la frecuencia del GC Menor es relativamente alta (no necesariamente se activa cuando el área del Edén está llena)
Generación Antigua
1. Los objetos que sigan vivos tras experimentar el reciclaje de basura N en la generación más joven serán colocados en la generación anterior. Por lo tanto, puede considerarse que la generación anterior se almacena en algunos objetos con un ciclo de vida largo.
2. La memoria también es mucho mayor que la de la nueva generación (la proporción aproximada es 1:2), cuando la memoria de la edad avanzada está llena, se activa la GC mayor, es decir, la GC completa, la frecuencia de la GC completa es relativamente baja, el tiempo de supervivencia del objeto de la edad avanzada es relativamente largo y la tasa de supervivencia es alta.
Generación permanente Se utiliza para almacenar archivos estáticos, como clases de Java, métodos, etc. Las generaciones persistentes no tienen un impacto significativo en la recolección de basura, pero algunas aplicaciones pueden generar o llamar dinámicamente a ciertas clases, como Hibernate, etc., y en este caso, se debe establecer un espacio de generación persistente relativamente grande para almacenar estas nuevas clases durante el tiempo de ejecución.
3. GC (Basurero)Coleccionistas utilizados por la nueva generación: Serial, PraNew, Parallel Scavenge Coleccionistas usados por los coleccionistas de la Edad Antigua: Serial Old, Parallel Old, CMS
![]()
Colector serial (algoritmo de replicación) La nueva generación de colectores de rosca simple, marcado y limpiado, son de una sola rosca, con la ventaja de ser simples y eficientes.
Serial Old Collector (algoritmo de acabado de etiqueta) Coleccionador de hilo único de la Antigüedad, versión antigua del Coleccionista de Serie.
Colector ParNew (algoritmo de copia stop-copy) El colector de nueva generación puede considerarse una versión multihilo del colector Serial, que ofrece mejor rendimiento que el Serial en un entorno de CPU multinúcleo.
Colector de Saqueo Paralelo (Algoritmo de Copia Stop-Copy) Colectores paralelos para alto rendimiento y utilización eficiente de la CPU. El rendimiento es generalmente del 99%, y el rendimiento = tiempo de hilo de usuario / (tiempo de hilo de usuario + tiempo de hilo GC). Es adecuado para escenarios como aplicaciones en segundo plano que no requieren una gran interacción correspondiente.
Colector antiguo paralelo (algoritmo de parada-copia) Una versión antigua del Parallel Scavenge collector, un colector paralelo, con prioridad de rendimiento
Colector CMS (Barrido de Marcas Concurrentes) (Algoritmo de Marca-Limpieza) Alta concurrencia, pausas bajas, búsqueda del tiempo de pausa de recuperación de GC más corto, alto uso de CPU, tiempo de respuesta rápido, tiempo de pausa corto y CPU multinúcleo que busca un tiempo de respuesta alto
4. El mecanismo de implementación de la GCDado que el objeto ha sido procesado en diferentes generaciones, el área y el momento de recogida de basura también son distintos. Hay dos tipos de GC: GC de Scavenge y GC completo.
Scavenge GC Normalmente, cuando se genera un nuevo objeto y no solicita espacio en Edén, se activa el GC de Saqueo, que GC en el área de Edén, elimina los objetos que no sobreviven y traslada los objetos supervivientes al área de Supervivientes. Luego resuelve las dos zonas de Survivor. De esta manera, la GC se realiza en la zona de Edén de la generación más joven y no afecta a la generación mayor. Como la mayoría de los objetos comienzan en el área del Edén, y el área del Edén no está asignada mucho, la GC del área del Edén se realiza con frecuencia. Por lo tanto, generalmente es necesario utilizar algoritmos rápidos y eficientes para que Eden sea libre lo antes posible.
Clasificación General Completa Organiza toda la pila, incluyendo Young, Tenured y Perm. La GC completa es más lenta que la GC de Scavenge porque requiere reciclar todo el montón, así que el número de GC completos debería reducirse al máximo. Una gran parte del proceso de afinación de JVM es la afinación de FullGC. La GC completa puede ser causada por las siguientes razones: 1. La titularidad se redacta completa 2. La permanente se escribe completa 3. System.gc() se muestra como una llamada 4. La estrategia de asignación de dominios de Heap cambia dinámicamente después de la última GC
5. Java con GC también tendrá problemas de fuga de memoria1. El uso de clases de colección estática como HashMap, Vector, etc., es la más propensa a fugas de memoria, y el ciclo de vida de estas variables estáticas es el mismo que el de la aplicación, y no se pueden liberar todos los objetos, ya que siempre serán aplicados por Vector y otros. Vector estático v = nuevo vector(); para (int i = 1; i<100; i++) { Objeto o = nuevo Objeto(); v.add(o); o = nulo; } En este ejemplo, existe una referencia v para el objeto Vector y una referencia o para el objeto Objeto en la pila de códigos. En el bucle For, seguimos generando nuevos objetos, luego añadiéndolos al objeto Vector y anulando la referencia o. La cuestión es, cuando la referencia o se anula, si ocurre GC, ¿puede el objeto objeto que creamos ser reciclado por GC? La respuesta es no. Porque cuando GC rastrea referencias en la pila de código, encuentra v referencias, y si sigues rastreando hacia abajo, verás que hay referencias a objetos Objeto en el espacio de memoria a los que apuntan las referencias v. Esto significa que, aunque la referencia o haya sido vaciada, aún quedan otras referencias al objeto Objeto a las que se puede acceder, por lo que la GC no puede liberarlas. Si después de este bucle, el objeto objeto no tiene efecto sobre el programa, entonces asumimos que el programa Java tiene una fuga de memoria. 2. Diversas conexiones, conexiones de base de datos, conexiones de red, conexiones de entrada/salida, etc., no muestran la llamada cerca del cierre y no son recicladas por GC, lo que resulta en fugas de memoria. 3. El uso de oyentes también puede causar fuga de memoria cuando el oyente no se elimina al liberar el objeto.
|