No hace mucho, entrevisté a algunos candidatos que buscaban trabajo como ingeniero senior de desarrollo de Java. A menudo los entrevisto y digo: "¿Puede presentarme algunas referencias débiles en Java?" Si el entrevistador dice: "Bueno, ¿está relacionado con la recolección de basura?", Estaré básicamente satisfecho y no lo haré. No espere que la respuesta sea la descripción de un artículo que llega al fondo de las cosas.
Sin embargo, contrariamente a lo esperado, me sorprendió descubrir que entre los casi 20 candidatos con un promedio de 5 años de experiencia en desarrollo y antecedentes altamente educados, solo dos personas sabían de la existencia de referencias débiles, pero solo una de estas dos personas realmente Infórmate sobre esto. Durante el proceso de la entrevista, también intenté provocar algunas cosas para ver si alguien de repente decía "Así que esto es todo", pero el resultado fue muy decepcionante. Comencé a preguntarme por qué se ignoraba tanto este conocimiento. Después de todo, las referencias débiles son una característica muy útil, y esta característica se introdujo cuando se lanzó Java 1.2 hace 7 años.
Bueno, no espero que te conviertas en un experto en referencias débiles después de leer este artículo, pero creo que al menos deberías entender qué son las referencias débiles, cómo usarlas y en qué escenarios se usan. Dado que son algunos conceptos desconocidos, explicaré brevemente las tres preguntas anteriores.
Referencia fuerte
La referencia fuerte es la referencia que usamos a menudo y está escrita de la siguiente manera:
Copie el código de código de la siguiente manera:
Búfer StringBuffer = nuevo StringBuffer();
Lo anterior crea un objeto StringBuffer y almacena una referencia (fuerte) a este objeto en el búfer variable. Sí, esta es una operación pediátrica (perdóneme por decir esto). Lo más importante de una referencia fuerte es que puede hacer que la referencia sea fuerte, lo que determina su interacción con el recolector de basura. Específicamente, si se puede acceder a un objeto a través de una cadena de enlaces de fuerte referencia (fuertemente accesible), no se reciclará. Esto es exactamente lo que necesitas si no quieres que se recicle el objeto con el que estás trabajando.
Pero las citas fuertes son tan fuertes
En un programa, es poco común establecer una clase como no extensible. Por supuesto, esto se puede lograr marcando la clase como final. O puede ser más complicado, que consiste en devolver una interfaz (Interfaz) a través de un método de fábrica que contiene un número desconocido de implementaciones específicas. Por ejemplo, queremos utilizar una clase llamada Widget, pero esta clase no se puede heredar, por lo que no se pueden agregar nuevas funciones.
Pero, ¿qué debemos hacer si queremos rastrear información adicional sobre el objeto Widget? Supongamos que necesitamos registrar el número de serie de cada objeto, pero debido a que la clase Widget no contiene este atributo y no se puede extender, no podemos agregar este atributo. De hecho, HashMap no tiene ningún problema y puede resolver completamente los problemas anteriores.
Copie el código de código de la siguiente manera:
serialNumberMap.put(widget, widgetSerialNumber);
Esto puede parecer bueno en la superficie, pero las fuertes referencias a objetos widget pueden causar problemas. Podemos estar seguros de que cuando ya no necesitemos un número de serie del widget, debemos eliminar la entrada del mapa. Si no lo eliminamos, puede causar pérdidas de memoria o podemos eliminar los widgets que estamos usando cuando los eliminamos manualmente, lo que puede provocar la pérdida de datos válidos. De hecho, estos problemas son muy similares. Este es un problema que los lenguajes sin mecanismos de recolección de basura a menudo encuentran al administrar la memoria. Pero no tenemos que preocuparnos por este problema, porque estamos usando el lenguaje Java con un mecanismo de recolección de basura.
Otro problema que pueden causar las referencias fuertes es el almacenamiento en caché, especialmente para archivos grandes como imágenes. Suponga que tiene un programa que necesita procesar imágenes proporcionadas por los usuarios. Un enfoque común es almacenar en caché los datos de las imágenes, porque cargar imágenes desde el disco es muy costoso y, al mismo tiempo, también queremos evitar tener dos copias de los mismos datos de la imagen. en la memoria al mismo tiempo.
El propósito del almacenamiento en caché es evitar que volvamos a cargar archivos innecesarios. Descubrirá rápidamente que el caché siempre contendrá una referencia a los datos de la imagen en la memoria. El uso de referencias sólidas obligará a que los datos de la imagen permanezcan en la memoria, lo que requiere que usted decida cuándo los datos de la imagen ya no son necesarios y los elimine manualmente de la memoria caché para que el recolector de basura pueda recuperarlos. Así que una vez más te ves obligado a hacer lo que hace el recolector de basura y decidir manualmente qué objetos limpiar.
Referencia débil
Una referencia débil es simplemente una referencia que no es tan fuerte para mantener el objeto en la memoria. Usando WeakReference, el recolector de basura lo ayudará a decidir cuándo se recicla el objeto al que se hace referencia y a eliminarlo de la memoria. Cree una referencia débil de la siguiente manera:
Copie el código de código de la siguiente manera:
eakReference<Widget>weakWidget = new WeakReference<Widget>(widget);
Puede obtener el objeto Widget real usando débilWidget.get(). Debido a que las referencias débiles no pueden evitar que el recolector de basura las recicle, encontrará que (cuando no hay una referencia fuerte al objeto widget), de repente se devuelve un valor nulo cuando se usa. conseguir.
La forma más sencilla de resolver el problema anterior de la grabación del número de secuencia del widget es utilizar la clase WeakHashMap integrada en Java. WeakHashMap es casi lo mismo que HashMap, la única diferencia es que WeakReference hace referencia a sus claves (¡no a sus valores!). Cuando una clave WeakHashMap se marca como basura, la entrada correspondiente a esta clave se eliminará automáticamente. Esto evita el problema anterior de eliminación manual de objetos Widget innecesarios. WeakHashMap se puede convertir fácilmente a HashMap o Map.
Cola de referencia
Una vez que un objeto de referencia débil comienza a devolver nulo, el objeto al que apunta la referencia débil se marca como basura. Y este objeto de referencia débil (no el objeto al que apunta) no sirve de nada. Por lo general, es necesario realizar algo de limpieza en este momento. Por ejemplo, WeakHashMap eliminará las entradas inútiles en este momento para evitar almacenar referencias débiles sin sentido que crezcan indefinidamente.
Las colas de referencias facilitan el seguimiento de referencias no deseadas. Cuando pasa un objeto ReferenceQueue al construir WeakReference, cuando el objeto señalado por la referencia está marcado como basura, el objeto de referencia se agregará automáticamente a la cola de referencia. A continuación, puede procesar la cola de referencia entrante en un período fijo, como realizar algún trabajo de limpieza para lidiar con estos objetos de referencia inútiles.
Cuatro tipos de referencias
En realidad, existen cuatro tipos de referencias con diferentes puntos fuertes en Java, de fuertes a débiles: referencias fuertes, referencias suaves, referencias débiles y referencias virtuales. La sección anterior presenta referencias fuertes y referencias débiles, y a continuación se describen las dos restantes, referencias suaves y referencias virtuales.
Referencia suave
Una referencia suave es básicamente lo mismo que una referencia débil, excepto que tiene una mayor capacidad para evitar que el período de recolección de basura recicle el objeto al que apunta que una referencia débil. Si se puede acceder a un objeto mediante una referencia débil, el recolector de basura destruirá el objeto en el siguiente ciclo de recolección. Pero si se puede alcanzar la referencia suave, entonces el objeto permanecerá en la memoria por más tiempo. El recolector de basura solo recuperará los objetos a los que pueden acceder estas referencias suaves cuando no haya memoria suficiente.
Dado que los objetos a los que se puede acceder mediante referencias suaves permanecerán en la memoria más tiempo que los objetos a los que se puede acceder mediante referencias débiles, podemos utilizar esta función para el almacenamiento en caché. De esta manera, puede guardar muchas cosas. El recolector de basura se preocupará por el tipo al que se puede acceder actualmente y el grado de consumo de memoria para el procesamiento.
Referencia fantasma
A diferencia de las referencias suaves y débiles, los objetos a los que apuntan las referencias virtuales son muy frágiles y no podemos obtener los objetos a los que apuntan mediante el método get. Su única función es que después de reciclar el objeto al que apunta, se agrega a la cola de referencia para registrar que el objeto al que apunta la referencia ha sido destruido.
Cuando el objeto señalado por una referencia débil se vuelve accesible mediante una referencia débil, la referencia débil se agrega a la cola de referencias. Esta operación ocurre antes de que realmente ocurra la destrucción de objetos o la recolección de basura. En teoría, el objeto que está a punto de reciclarse se puede resucitar mediante un método destructor que no cumpla con las normas. Pero esta débil referencia será destruida. Una referencia virtual se agrega a la cola de referencias solo después de que el objeto al que apunta se elimina de la memoria. Su método get sigue devolviendo nulo para evitar que el objeto casi destruido al que apunta resucite.
Hay dos escenarios de uso principales para las referencias virtuales. Le permite saber exactamente cuándo se elimina de la memoria el objeto al que hace referencia. Y en realidad esta es la única forma en Java. Esto es especialmente cierto cuando se trata de archivos grandes, como imágenes. Cuando determina que un objeto de datos de imagen debe reciclarse, puede usar referencias virtuales para determinar si el objeto se recicla antes de continuar cargando la siguiente imagen. De esta manera podrás evitar en la medida de lo posible terribles errores de desbordamiento de memoria.
El segundo punto es que las referencias virtuales pueden evitar muchos problemas durante la destrucción. El método de finalización puede resucitar objetos que están a punto de ser destruidos creando fuertes referencias que apunten a ellos. Sin embargo, un objeto que anula el método de finalización debe pasar por dos ciclos de recolección de basura separados si desea reciclarse. En el primer ciclo, un objeto se marca como reciclable y luego puede destruirse. Sino porque todavía existe una pequeña posibilidad de que este objeto resucite durante el proceso de destrucción. En este caso, el recolector de basura debe ejecutarse nuevamente antes de que el objeto sea realmente destruido. Debido a que la destrucción puede no ser muy oportuna, es necesario realizar una cantidad indeterminada de ciclos de recolección de basura antes de que se llame a la destrucción del objeto. Esto significa que puede haber un gran retraso en la limpieza del objeto. Es por eso que todavía recibes molestos errores de falta de memoria cuando la mayor parte del montón está marcado como basura.
Usando referencias virtuales, la situación anterior se resolverá. Cuando se agrega una referencia virtual a la cola de referencias, no hay absolutamente ninguna forma de obtener un objeto destruido. Porque en este momento, el objeto ha sido destruido de la memoria. Debido a que una referencia virtual no se puede usar para regenerar el objeto al que apunta, su objeto se limpiará en el primer ciclo de recolección de basura.
Obviamente, no se recomienda anular el método de finalización. Debido a que las referencias virtuales son obviamente seguras y eficientes, eliminar el método de finalización puede simplificar significativamente la máquina virtual. Por supuesto, también puedes anular este método para lograr más. Todo se reduce a una elección personal.
Resumir
Creo que al ver esto, muchas personas comienzan a quejarse. ¿Por qué estás hablando de una API antigua de los últimos diez años? Bueno, según mi experiencia, muchos programadores de Java no conocen muy bien este conocimiento. -Es necesaria una comprensión profunda y espero que todos puedan obtener algo de este artículo.