Object Lifetime: How Objects Are Created and Destroyed
An instance of a class, an object, is created by using the New keyword. Initialization tasks often must be performed on new objects before they are used. Common initialization tasks include opening files, connecting to databases, and reading values of registry keys. Visual Basic controls the initialization of new objects using procedures called constructors (special methods that allow control over initialization).
After an object leaves scope, it is released by the common language runtime (CLR). Visual Basic controls the release of system resources using procedures called destructors. Together, constructors and destructors support the creation of robust and predictable class libraries.
Sub New and Sub Finalize
The Sub New and Sub Finalize procedures in Visual Basic initialize and destroy objects; they replace the Class_Initialize and Class_Terminate methods used in Visual Basic 6.0 and earlier versions. Unlike Class_Initialize, the Sub New constructor can run only once when a class is created. It cannot be called explicitly anywhere other than in the first line of code of another constructor from either the same class or from a derived class. Furthermore, the code in the Sub New method always runs before any other code in a class. Visual Basic 2005 and later versions implicitly create a Sub New constructor at run time if you do not explicitly define a Sub New procedure for a class.
Before releasing objects, the CLR automatically calls the Finalize method for objects that define a Sub Finalize procedure. The Finalize method can contain code that needs to execute just before an object is destroyed, such as code for closing files and saving state information. There is a slight performance penalty for executing Sub Finalize, so you should define a Sub Finalize method only when you need to release objects explicitly.
The garbage collector in the CLR does not (and cannot) dispose of unmanaged objects, objects that the operating system executes directly, outside the CLR environment. This is because different unmanaged objects must be disposed of in different ways. That information is not directly associated with the unmanaged object; it must be found in the documentation for the object. A class that uses unmanaged objects must dispose of them in its Finalize method.
The Finalize destructor is a protected method that can be called only from the class it belongs to, or from derived classes. The system calls Finalize automatically when an object is destroyed, so you should not explicitly call Finalize from outside of a derived class's Finalize implementation.
Unlike Class_Terminate, which executes as soon as an object is set to nothing, there is usually a delay between when an object loses scope and when Visual Basic calls the Finalize destructor. Visual Basic 2005 and later versions allow for a second kind of destructor, Dispose, which can be explicitly called at any time to immediately release resources.
A Finalize destructor should not throw exceptions, because they cannot be handled by the application and can cause the application to terminate.
Class instances often control resources not managed by the CLR, such as Windows handles and database connections. These resources must be disposed of in the Finalize method of the class, so that they will be released when the object is destroyed by the garbage collector. However, the garbage collector destroys objects only when the CLR requires more free memory. This means that the resources may not be released until long after the object goes out of scope.
To supplement garbage collection, your classes can provide a mechanism to actively manage system resources if they implement the IDisposable interface. IDisposable has one method, Dispose, which clients should call when they finish using an object. You can use the Dispose method to immediately release resources and perform tasks such as closing files and database connections. Unlike the Finalize destructor, the Dispose method is not called automatically. Clients of a class must explicitly call Dispose when you want to immediately release resources.
A class that implements the IDisposable interface should include these sections of code:
A field for keeping track of whether the object has been disposed:
Protected disposed As Boolean = False
Protected Overridable Sub Dispose(ByVal disposing As Boolean) If Not Me.disposed Then If disposing Then ' Insert code to free managed resources. End If ' Insert code to free unmanaged resources. End If Me.disposed = True End Sub
An implementation of Dispose that contains only the following code:
Public Sub Dispose() Implements IDisposable.Dispose Dispose(True) GC.SuppressFinalize(Me) End Sub
An override of the Finalize method that contains only the following code:
Protected Overrides Sub Finalize() Dispose(False) MyBase.Finalize() End Sub
Deriving from a Class that Implements IDisposable
A class that derives from a base class that implements the IDisposable interface does not need to override any of the base methods unless it uses additional resources that need to be disposed. In that situation, the derived class should override the base class's Dispose(disposing) method to dispose of the derived class's resources. This override must call the base class's Dispose(disposing) method.
Protected Overrides Sub Dispose(ByVal disposing As Boolean) If Not Me.disposed Then If disposing Then ' Insert code to free managed resources. End If ' Insert code to free unmanaged resources. End If MyBase.Dispose(disposing) End Sub
A derived class should not override the base class's Dispose and Finalize methods. When those methods are called from an instance of the derived class, the base class's implementation of those methods call the derived class's override of the Dispose(disposing) method.
The following diagram shows which methods are inherited and which methods are overridden in the derived class.
When this DisposeFinalize pattern is followed, the resources of the derived class and base class are correctly disposed. The following diagram shows which methods get called when the classes are disposed and finalized.
Garbage Collection and the Finalize Destructor
The .NET Framework uses the reference-tracing garbage collection system to periodically release unused resources. Visual Basic 6.0 and earlier versions used a different system called reference counting to manage resources. Although both systems perform the same function automatically, there are a few important differences.
The CLR periodically destroys objects when the system determines that such objects are no longer needed. Objects are released more quickly when system resources are in short supply, and less frequently otherwise. The delay between when an object loses scope and when the CLR releases it means that, unlike with objects in Visual Basic 6.0 and earlier versions, you cannot determine exactly when the object will be destroyed. In such a situation, objects are said to have non-deterministic lifetime. In most cases, non-deterministic lifetime does not change how you write applications, as long as you remember that the Finalize destructor may not immediately execute when an object loses scope.
Another difference between the garbage-collection systems involves the use of Nothing. To take advantage of reference counting in Visual Basic 6.0 and earlier versions, programmers sometimes assigned Nothing to object variables to release the references those variables held. If the variable held the last reference to the object, the object's resources were released immediately. In later versions of Visual Basic, while there may be cases in which this procedure is still valuable, performing it never causes the referenced object to release its resources immediately. To release resources immediately, use the object's Dispose method, if available. The only time you should set a variable to Nothing is when its lifetime is long relative to the time the garbage collector takes to detect orphaned objects.