2010-09-09 12 views
13

Tengo un problema con pérdidas de memoria en mi aplicación de servicio .NET Windows. Así que comencé a leer artículos sobre administración de memoria en .NET. Y he encontrado una práctica interesante en one of Jeffrey Richter articles. Este nombre de práctica es "resurrección de objetos". Parece que situar código que inicializa la variable global o estática a "este":Usos de resurrección de objetos

protected override void Finalize() { 
    Application.ObjHolder = this; 
    GC.ReRegisterForFinalize(this); 
} 

entiendo que esta es una mala práctica, sin embargo me gustaría saber patrones que utiliza esta práctica. Si conoce alguno, por favor escriba aquí.

+3

Si un desarrollador que trabaja para mí escribió ese código, me gustaría que lo destruyan. –

+0

@John: Estoy completamente de acuerdo, es realmente algo para casos extremos, IMO. –

+3

@John: Lo entiendo claramente y mi interés es simplemente científico :) – Vokinneberg

Respuesta

5

especulativo: en una situación de piscina, como la ConnectionPool.

Puede usarlo para reclamar objetos que no se eliminaron correctamente pero a los que el código de la aplicación ya no contiene una referencia. No puede mantenerlos en una lista en el grupo porque eso bloquearía la recopilación de GC.

+0

Sí, la primera idea fue objeto agrupado con couter interno de tiempos para ser resis tivo, por ejemplo. Cuando el contador está abajo a 0, la última Finalización debe ser suprimida y el objeto está muriendo. Pero de todos modos, creo que no es una buena implementación de la agrupación de todos modos. – Vokinneberg

+0

@Vokin, la estrategia de contador no es la única forma de administrar la vida útil aquí. Creo que el punto principal es reclamar un recurso del GC. –

3

El único lugar que puedo pensar en usar esto, potencialmente, sería cuando intentabas limpiar un recurso, y la limpieza del recurso falló. Si era fundamental volver a intentar el proceso de limpieza, técnicamente podría "volver a registrar" el objeto que se va a finalizar, que con suerte tendría éxito, la segunda vez.

Dicho esto, evitaría esto en la práctica.

7

Del mismo artículo: "Hay muy pocos buenos usos de la resurrección, y realmente debería evitarla si es posible".

El mejor uso que puedo pensar es un patrón de "reciclaje". Considere una fábrica que produce objetos caros, prácticamente inmutables; por ejemplo, objetos instanciados al analizar un archivo de datos, o al reflejar un ensamblaje, o al copiar profundamente un gráfico de objetos "maestro". Es poco probable que los resultados cambien cada vez que realice este costoso proceso. Le conviene evitar la creación de instancias desde cero; sin embargo, por algunas razones de diseño, el sistema debe poder crear muchas instancias (sin singletons), y sus consumidores no pueden conocer la fábrica para que puedan "devolver" el objeto ellos mismos; pueden tener el objeto inyectado o recibir un delegado de método de fábrica del que obtienen una referencia. Cuando la clase dependiente sale del alcance, normalmente la instancia también lo haría.

Una posible respuesta es anular Finalize(), limpiar cualquier parte del estado mutable de la instancia, y luego, mientras la fábrica esté dentro del alcance, vuelva a conectar la instancia a algún miembro de la fábrica. Esto permite que el proceso de recolección de basura, en efecto, "recicle" la valiosa porción de estos objetos cuando de otro modo saldrían del alcance y se destruirían por completo. La fábrica puede mirar y ver si tiene algún objeto reciclado disponible en su "contenedor", y si es así, puede pulirlo y distribuirlo. La fábrica solo tendría que crear una instancia de una nueva copia del objeto si la cantidad total de objetos en uso aumentaba.

Otros posibles usos pueden incluir una implementación altamente especializada de registrador o auditoría, donde los objetos que desea procesar después de su muerte se unirán a una cola de trabajos administrada por este proceso. Después de que el proceso los maneja, pueden ser totalmente destruidos.

En general, si desea que los dependientes PIENSE que se están deshaciendo de un objeto, o para no tener que molestarse, pero desea conservar la instancia, la resurrección puede ser una buena herramienta, pero tendrá que Mírelo MUY cuidadosamente para evitar situaciones en las que los objetos que reciben referencias resucitadas se conviertan en "ratas paquete" y conserve cada instancia que se haya creado en la memoria durante la vida del proceso.

+0

El reciclaje de objetos podría (y probablemente debería) hacerse sin resurrección. Creo que un caso de uso más grande es cuando un objeto tiene una acción de limpieza que solo debe realizarse cuando no existen otras referencias a él en cualquier parte del universo (incluso dentro de objetos con finalizadores). El objeto a limpiar puede contener una referencia a un objeto "más limpio" que puede crear una referencia larga débil estática al objeto que necesita limpieza. El finalizador del objeto más limpio puede verificar si la referencia débil larga ha muerto; si es así, debe destruir la referencia estática y realizar la limpieza; – supercat

+0

si la referencia débil no ha muerto, el objeto más limpio debe volver a registrarse para la finalización (resucitándose de manera efectiva, al menos temporalmente). Tenga en cuenta que si la única referencia a 'WeakReference' estuviera en uno de los campos del limpiador, la' WeakReference' podría morir incluso mientras su objetivo todavía estuviera vivo; el limpiador debe crear una referencia enraizada estática a 'WeakReference' para garantizar que solo muera cuando lo haga su objetivo. – supercat

3

Un hermano mío trabajó una vez en una plataforma de simulación de alto rendimiento.Me contó cómo, en la aplicación, la construcción de objetos era un cuello de botella demostrable para el rendimiento de la aplicación. Parecería que los objetos eran grandes y requirieron algún proceso significativo para inicializarse.

Implementaron un repositorio de objetos para contener instancias de objetos "retirados". Antes de construir un nuevo objeto, primero verificaban si ya existía en el repositorio.

El trade-off fue el aumento del consumo de memoria (ya que puede haber muchos objetos sin usar a la vez) para un mayor rendimiento (ya que se redujo el número total de construcciones de objetos).

Tenga en cuenta que la decisión de implementar este patrón se basó en los cuellos de botella que observaron a través del perfilado en su escenario específico. Esperaría que esto sea una circunstancia excepcional.

0

Por lo que sé, .net llama a los finalizadores en un orden no específico. Si su clase contiene referencias a otros objetos, podrían haber sido finalizados (y por lo tanto Dispuestos) cuando se llame a su finalizador. Si luego decides resucitar tu objeto, tendrás referencias a objetos finalizados/eliminados.

class A { 
    static Set<A> resurectedA = new Set<A>(); 
    B b = new B(); 
    ~A() { 
    //will not die. keep a reference in resurectedA. 
    resurectedA.Add(this); 
    GC.ReRegisterForFinalize(this); 

    //at this point you may have a problem. By resurrecting this you are resurrecting b and b's Finalize may have already been called. 
    } 
} 
class B : IDisposable { 
    //regular IDisposable/Destructor pattern http://msdn.microsoft.com/en-us/library/b1yfkh5e(v=vs.110).aspx 
}