Destrucción de objetos y recolección de basura

Destrucción de objetos y recolección de basura

La destrucción de objetos y liberación de recursos en VB6 está basada en una técnica que mantiene un contador interno de las referencias que en el código cliente hay establecidas hacia un objeto; cuando la última referencia es eliminada, se ejecuta su evento Terminate( ), y el objeto se destruye. Este sistema

de destrucción se denomina finalización determinista, ya que nos permite saber el momento preciso en el que un objeto es destruido.

A pesar de ser efectiva, la finalización determinista de VB6 tiene como problema principal el hecho de que si existen dos objetos que mantienen una referencia recíproca entre ellos, lo que se denomina una referencia circular, dichos objetos pueden quedar en memoria permanentemente durante toda la ejecución del programa, a pesar de no ser ya utilizados; este contratiempo se produce básicamente en la programación de componentes, cuando los objetos provienen de componentes distintos.

El esquema de destrucción de objetos en la plataforma .NET, para evitar las referencias circulares entre objetos, no utiliza conteo de referencias, sino que en su lugar, implementa un sistema de búsqueda y eliminación de objetos que ya no están siendo utilizados, denominado recolección de basura (garbage collection).

Cada ciertos periodos de tiempo dictados por el entorno, el recolector de basura se activa y realiza una exploración entre los objetos existentes, destruyendo aquellos que no están siendo utilizados. Este es un proceso controlado automáticamente por el entorno de .NET.

Dado que mediante la recolección de basura, no podemos predecir el momento exacto en el que el objeto es destruido, y la memoria que utiliza es liberada, a este modelo de gestión de recursos se le denomina finalización no determinista.

Además de evitar el problema de las referencias circulares, el recolector de basura está diseñado para realizar su trabajo en los momentos de menor actividad del sistema, para que el impacto en el rendimiento general sea el menor posible.

Como ya hemos mencionado, el recolector de basura funciona de modo automático; no obstante, si en un determinado momento queremos forzar su uso, la jerarquía de .NET nos provee de la clase GC (Garbage Collector), que representa al objeto recolector, pudiendo manipularlo a través de sus miembros compartidos.

Por ejemplo, si necesitamos que se efectúe la recolección de objetos que no estén siendo utilizados ejecutaríamos el método Collect( ) de este objeto como vemos en el Código fuente 272.

GC.Collect()

Código fuente 272

Programación con Visual Basic .NET © Grupo EIDOS

A pesar de tener acceso al recolector de basura del sistema, se recomienda no abusar de su utilización en nuestro código, ya que consume una importante cantidad de recursos cuando está en ejecución. Lo más adecuado es dejar esta labor en manos de la propia plataforma, que activará el proceso de recolección en los momentos más apropiados.

Cuando en VB6 asignamos Nothing a un objeto durante la ejecución, provocamos explícitamente la destrucción inmediata del objeto. Durante ese proceso se ejecuta su evento Terminate( ), en el que podemos escribir código para eliminar elementos que pudiera estar utilizando a su vez el objeto: cierre

de conexiones a bases de datos, ficheros, etc. A este tipo de evento, que se ejecuta justo antes de la destrucción de un objeto se le denomina finalizador.

En .NET Framework los objetos también disponen de métodos finalizadores. Para implementar este tipo de miembro en nuestras clases, escribiremos un método con el nombre Finalize( ), de ámbito Protected, que sobrescriba y llame al finalizador de su clase padre. Podemos abrir la lista de métodos en el editor de código del IDE, en donde encontraremos este método preconstruido, que tendremos que completar con el código necesario. Ver el Código fuente 273.

Protected Overrides Sub Finalize() MyBase.Finalize() ' operaciones de finalización del objeto ' ....

End Sub

Código fuente 273

Sin embargo, debido al sistema de gestión de recursos que implementa la finalización no determinista, no podemos saber con tanta precisión el momento en el que un objeto es destruido.

Cuando asignamos Nothing a un objeto, o la variable que lo contiene pierde su ámbito y es eliminada, transcurre un tiempo que no es posible determinar hasta que el objeto es definitivamente destruido. Esta circunstancia hace que la eliminación de los elementos usados por el propio objeto pueda demorarse hasta varios minutos, con lo que una conexión a un origen de datos que realizara un excesivo consumo de recursos podría estar abierta un tiempo innecesario. El uso de un evento finalizador en esta situación no sería muy efectivo.

La solución a este problema pasa por crear un método, en el cuál realizaríamos las tareas de finalización que no fuera conveniente dejar en el método Finalize( ) de la clase. La denominación para este método puede elegirla el lector libremente, pero por convención, se recomienda asignarle el nombre Dispose( ), ya que este es el nombre que utilizan el resto de clases de la plataforma. Ver el Código fuente 274.

Module Module1 Sub Main() Dim loEmple As Empleado loEmple = New Empleado() ' .... ' .... loEmple.Dispose() loEmple = Nothing ' a partir de aquí, en cualquier momento el entorno ' activará el recolector de basura, que ejecutará ' el evento Finalize() y destruirá el objeto ' .... ' ....

© Grupo EIDOS 20. Elementos compartidos e interfaces

End Sub End Module

Public Class Empleado ' .... ' .... Public Sub Dispose()

' en este método escribiremos las tareas ' de finalización más urgentes: ' cierre de conexiones, manipuladores de ficheros, etc ' que no sea conveniente dejar en Finalize() ' ....

End Sub End Class

Código fuente 274