Un excelente programador de Java debe comprender cómo funciona GC, cómo optimizar el rendimiento de GC y cómo tener una interacción limitada con GC, porque algunas aplicaciones tienen requisitos de alto rendimiento, como sistemas integrados, sistemas en tiempo real, etc., que solo pueden ser mejorado exhaustivamente. Este artículo primero presenta brevemente el principio de funcionamiento de GC, luego realiza discusiones en profundidad sobre varios temas clave de GC, y finalmente presenta algunas sugerencias de programación de Java para mejorar el rendimiento de los programas Java desde la perspectiva de GC.
Los principios básicos de GC
La gestión de la memoria de Java es en realidad la gestión de objetos, incluida la asignación y la liberación de objetos.
Para los programadores, la nueva palabra clave se usa para asignar objetos; .GC será responsable de reclamar el espacio de memoria de todos los objetos /"inalcanzables /".
Para GC, cuando un programador crea un objeto, GC comienza a monitorear la dirección, el tamaño y el uso del objeto. En general, GC utiliza gráficos dirigidos para registrar y administrar todos los objetos en el montón (montón) (consulte la referencia 1 para más detalles). De esta manera, qué objetos son /"accesibles /" y qué objetos son /"inalcanzables /". Sin embargo, para garantizar que GC se pueda implementar en diferentes plataformas, las especificaciones de Java no estipulan estrictamente muchos comportamientos de GC. Por ejemplo, no hay regulaciones claras sobre temas importantes, como qué tipo de algoritmo de reciclaje usar y cuándo realizar el reciclaje. Por lo tanto, los diferentes implementadores de JVM a menudo tienen diferentes algoritmos de implementación. Esto también aporta incertidumbre al desarrollo de los programadores de Java. Este artículo examina varios temas relacionados con el trabajo de GC y se esfuerza por reducir el impacto negativo de esta incertidumbre en los programas Java.
GC incremental (GC incremental)
GC generalmente se implementa en JVM por uno o un grupo de procesos. Por lo tanto, cuando el GC se ejecuta durante mucho tiempo, el usuario puede sentir la pausa del programa Java. Aún así, muchos objetos que deben reciclar que no se han reciclado. Por lo tanto, al diseñar GC, es necesario sopesar el tiempo de pausa y la tasa de recuperación. Una buena implementación de GC permite a los usuarios definir la configuración que necesitan. Otros juegos en línea en tiempo real no pueden permitir interrupciones a largo plazo al programa. El GC incremental es dividir una interrupción a largo plazo en muchas pequeñas interrupciones a través de un cierto algoritmo de reciclaje, reduciendo así el impacto de GC en los programas de usuarios. Aunque GC incremental puede no ser tan eficiente como un GC regular en el rendimiento general, puede reducir el tiempo máximo de pausa del programa.
El HotSpot JVM proporcionado por Sun JDK puede soportar GC incremental. La implementación de GC incremental JVM de HotSpot es usar el algoritmo de GC de tren. Su idea básica es agrupar todos los objetos en el montón (jerárquicamente) de acuerdo con la creación y el uso, poner objetos con un uso frecuente y altamente relevante en una cola, y seguir agrupando a medida que el programa ejecuta el ajuste. Cuando GC funciona, siempre recicla los objetos más antiguos (rara vez visitados recientemente) primero, y si todo el grupo es objetos reciclables, GC recicla todo el grupo. De esta manera, cada ejecución de GC solo reciclará una cierta proporción de objetos inalcanzables para garantizar el funcionamiento sin problemas del programa.
Explicación detallada de la función finalizar
Finalize es un método ubicado en la clase de objetos. Dado que la función finalización no implementa automáticamente las llamadas de la cadena, debemos implementarlas manualmente, por lo que la última declaración de la función finalización suele ser super.Finalize (). De esta manera, podemos implementar la llamada para implementar finalizar de abajo hacia arriba, es decir, primero liberar nuestros propios recursos y luego liberar los recursos de la clase principal.
Según la especificación del lenguaje Java, el JVM asegura que este objeto no se pueda apagar antes de llamar a la función finalización, pero el JVM no garantiza que esta función se llame. Además, la especificación también garantiza que la función finalización se ejecute como máximo una vez.
Muchos principiantes de Java pensarán que este método es similar al destructor en C ++, y pone la liberación de muchos objetos y recursos en esta función. En realidad, esta no es una buena manera. Primero hay tres razones. En segundo lugar, después de completar la ejecución final, el objeto puede ser accesible y GC necesita verificar nuevamente si el objeto es accesible. Por lo tanto, el uso de finalización reducirá el rendimiento de GC. Tercero, desde el momento en que GC Llamadas finalice es incierto, también es incierto liberar recursos de esta manera.
En general, la finalización se utiliza para liberar algunos recursos que no son fáciles de controlar y son muy importantes, como algunas operaciones de E/S y conexiones de datos. El lanzamiento de estos recursos es muy crítico para toda la aplicación. En este caso, los programadores deben administrar principalmente estos recursos a través del programa en sí (incluida la liberación) y complementar el método de liberar los recursos con la función finalización como un suplemento para formar un mecanismo de gestión de seguros dual, en lugar de depender únicamente de finalizar para liberar recursos.
Aquí hay un ejemplo para ilustrar que después de que se llama la función finalización, aún puede ser accesible.
clase MyObject {Test Main; /Restaurar este objeto para que este objeto pueda llegar a System.out.println (/"Esto es finalize/"); // utilizado para probar para ejecutar solo una vez}} prueba de clase {myobject ref; ] args) {test test = new test (); .ref! = NULL) System.out.println (/"Mi objeto sigue vivo/");
Resultados de ejecución:
Esto es finalizar
MyObject sigue vivo
En este ejemplo, debe tenerse en cuenta que, aunque el objeto MyObject se convierte en un objeto accesible en Finalize, la próxima vez que recicle, finalice ya no se llamará porque la función finalización solo se llama una vez como máximo.
Cómo interactúa el programa con GC
Java2 mejora las funciones de gestión de memoria y agrega un paquete java.lang.ref, que define tres clases de referencia. Estas tres clases de referencia son Softreference, WeakReference y Phantomreference. Las fortalezas de referencia de estas clases de referencia son entre objetos accesibles e inalcanzables.
También es muy fácil crear un objeto de referencia. Establezca la referencia normal a NULL. Al mismo tiempo, llamamos a este objeto un objeto de referencia suave.
La característica principal de la referencia suave es que tiene fuertes funciones de citas. Este tipo de memoria se recicla solo cuando no hay memoria insuficiente, por lo que cuando hay suficiente memoria, generalmente no se recicla. Además, estos objetos de referencia también se pueden configurar en NULL antes de que Java lanza la excepción de Memory. El siguiente es el siguiente pseudocódigo para este tipo de referencia;
// Aplicación Un objeto de imagen Imagen de imagen = nueva imagen (); // Crear objeto de imagen ... // Use imagen ... // Después de usar la imagen, establecerla en el tipo de referencia suave y liberar una referencia fuerte; = nuevo Softreference (imagen); necesita ser recargado;
La mayor diferencia entre los objetos de referencia débiles y los objetos de referencia suaves es que cuando GC recicla, necesita usar un algoritmo para verificar si los objetos de referencia suave se recicla, mientras que GC siempre recicla objetos de referencia débiles. Los objetos de referencia débiles son más fáciles y rápidos de ser reciclados por GC. Aunque GC debe reciclar objetos débiles cuando se ejecutan, un grupo de objetos débiles con relaciones complejas a menudo requiere varias corridas de GC para completar. Los objetos de referencia débiles a menudo se usan en las estructuras de mapas para referirse a objetos con grandes volúmenes de datos.
Las referencias fantasmas son menos útiles y se utilizan principalmente para ayudar en el uso de funciones finalizadas. Los objetos fantasmas se refieren a algunos objetos que han completado la función finalización y son objetos inalcanzables, pero GC no los ha reciclado. Este tipo de objeto puede ayudar a finalizar en algún trabajo de reciclaje posterior.
Algunas sugerencias de codificación de Java
Según el principio de funcionamiento de GC, podemos usar algunas habilidades y métodos para que GC se ejecute de manera más eficiente y más en línea con los requisitos de la aplicación. Aquí hay algunas sugerencias para la programación.
1. La sugerencia más básica es liberar las referencias de objetos inútiles lo antes posible. Cuando la mayoría de los programadores usan variables temporales, establecerán automáticamente la variable de referencia en NULL después de salir del dominio activo (alcance). Los gráficos, etc., tienen una relación compleja entre estos objetos. Para tales objetos, GC los recicla generalmente son menos eficientes. Si el programa lo permite, asigne el objeto de referencia no utilizado a NULL lo antes posible. [Página]
2. Intente usar la función finalizar lo menos posible. La función finalización es una oportunidad para que Java brinde a los programadores la oportunidad de liberar objetos o recursos. Sin embargo, aumentará la carga de trabajo de GC, así que trate de usar la finalización de la menor cantidad posible para reciclar recursos.
3. Si necesita usar imágenes de uso frecuente, puede usar el tipo de aplicación suave. Puede guardar la imagen en la memoria tanto como sea posible para que el programa llame sin causar OutOfMemory.
4. Presta atención a los tipos de datos de recopilación, incluidas las estructuras de datos, como matrices, árboles, gráficos y listas vinculadas. Además, preste atención a algunas variables globales, así como a algunas variables estáticas. Estas variables tienden a causar fácilmente referencias colgantes, causando desechos de memoria.
5. Cuando el programa tiene un cierto tiempo de espera, el programador puede ejecutar manualmente System.gc () para notificar a GC que se ejecute, pero la especificación de lenguaje Java no garantiza que GC ejecute. El uso de GC incremental puede acortar el tiempo de pausa de los programas Java.