2009-08-03 5 views
6

Entiendo que en un lenguaje administrado como Java o C# hay una cosa llamada recolector de basura que de vez en cuando comprueba si hay cualquier instancia de objeto a la que ya no se hace referencia y, por lo tanto, queda completamente huérfana, y luego se borra de la memoria. Pero si dos objetos no son referenciados por ninguna variable en un programa, sino que se referencian entre sí (como una suscripción a un evento), el recolector de basura verá esta referencia y no borrará los objetos de la memoria.¿Por qué el recolector de basura no puede darse cuenta cuando los objetos que se referencian entre sí son realmente huérfanos?

¿Por qué es esto? ¿Por qué el recolector de basura no puede darse cuenta de que ninguno de los objetos puede ser referenciado por ninguna parte activa del programa en ejecución y deshacerse de ellos?

+5

De hecho, este es uno de los problemas con los entornos de conteo de referencias que ** se ocupa ** en .NET GC. –

+0

Otra característica tradicional de Lisp que se abre camino en los entornos principales. –

Respuesta

24

Su presunción es incorrecta. Si el GC 've' (ver más abajo) una referencia cíclica de 2 o más objetos (durante la fase 'Marcar') pero no se referencia por ningún otro objeto o manija GC permanente (referencias fuertes), esos objetos se recogerán (durante la fase 'Barrido').

Puede encontrar una descripción en profundidad del recolector de basura CLR en this MSDN article y en this blog post.

Nota: De hecho, el GC no incluso 'ver' estos tipos de objetos durante la fase de marca ya que son inalcanzables, y por lo tanto conseguir recogido durante una barrer.

+0

Bien, eso hace las cosas mucho más fáciles. Por lo tanto, mi programa tiene una estructura de árbol compuesta de Nodos, cada uno de los cuales se muestra a través de un TreeNode personalizado en una vista de árbol. Si nada hace referencia al Nodo o al TreeNode, entonces no tengo que preocuparme por separar los eventos o las referencias entre ellos. –

7

La mayoría de los GC ya no funcionan con el recuento de referencias. Usualmente (y este es el caso tanto en Java como en .NET) trabajan con la capacidad de alcance desde el conjunto raíz de objetos. El conjunto raíz de objetos son globales y las instancias con referencia a la pila. Todo lo que se puede acceder desde ese conjunto directa o indirectamente está vivo. El resto de la memoria es inalcanzable y, por lo tanto, propensa a ser recopilada.

+0

En realidad, creo que es incorrecto llamar al Recuento de referencias una forma de Recolección de basura. Son 2 formas separadas de administración de memoria. –

+0

El recuento de referencias (RC) es la forma de recolección de basura (GC). El algoritmo RC simple no puede tratar con referencias cíclicas, pero sí la técnica de Bobrow, el algoritmo de puntero débil y los algoritmos parciales de barrido de marca que pueden hacerlo. – mtasic85

2

Me gustaría agregar que los problemas relacionados con la suscripción a eventos generalmente giran en torno al hecho de que el suscriptor & editorial tiene ciclos de vida muy diferentes.

Adjunte usted p. Ej. al evento App.Idle en Windows Forms y su objeto se mantendrá activo durante la vida útil restante de la aplicación. ¿Por qué? Esa aplicación estática tendrá una referencia (aunque indirectamente a través de un delegado) a su observador registrado. A pesar de que puede haber eliminado a su observador, todavía está adjunto a App.Idle. Puedes construir muchos de esos ejemplos.

1

Las otras respuestas aquí son ciertamente correctas; .NET lo hace como recolección de basura basada en la accesibilidad de un objeto.

Lo que quería agregar: puedo recomendar leer Understanding Garbage Collection in .NET (artículo de charlas sencillas de Andrew Hunter) si desea obtener información un poco más detallada.

2

Este es un inconveniente importante del tradicional reference counting garbage collection. La propiedad de un recolector de basura que describe este comportamiento es un recolector incompleto. Otros colectores entran en gran parte en una categoría llamada que rastrea los recolectores de basura, que incluyen híbridos tradicionales de barrido de marca, semi-espacio/compactación e híbridos generacionales, y no sufren estos inconvenientes (pero se enfrentan a muchos otros).

Todas las implementaciones de JVM y CLI que conozco usan colectores completos, lo que significa que no sufren el problema específico que usted está preguntando aquí. Que yo sepa, de esos Jikes RVM es el único que suministra un colector de recuento de referencia (uno de los muchos).

Otra cosa interesante a tener en cuenta es que hay soluciones para el problema de completitud en el conteo de referencias de recolección de basura, y el resulting collectors muestra algunas propiedades de rendimiento interesantes que son difíciles de eliminar de los colectores. Desafortunadamente, los algoritmos de recolección de basura de mayor rendimiento y las modificaciones más completas dependen de la asistencia del compilador, por lo que llevarlos a C++ 's shared_ptr<T> es difícil/no ocurre. En cambio, tenemos weak_ptr<T> y documented rules (disculpe el vínculo subóptimo, aparentemente la documentación se me escapa) acerca de simplemente evitar los problemas. Este isn't the first time (otro enlace mediocre) que hemos visto este enfoque, y la esperanza es el trabajo extra para evitar problemas de memoria es menor que la cantidad de trabajo necesario para mantener el código que no utiliza shared_ptr<T>, etc.

El los enlaces mediocres se deben a que gran parte de mi material de referencia está disperso en notas de la clase de gestión de memoria del último semestre.

Cuestiones relacionadas