When a program starts, the system allocates a chunk of memory for the program called the managed heap. When it allocates data for reference types (class objects), Visual Basic uses memory from this heap. (For more information about the stack and heap and their relative performance, see the section “Heap and Stack Performance” earlier in this chapter.)
When the program no longer needs to use a reference object, Visual Basic does not mark the heap memory as free for later use. If you set a reference variable to Nothing so that no variable points to the object, the object’s memory is no longer available to the program, but Visual Basic does not reuse the object’s heap memory, at least not right away.
The optimizing engine of the garbage collector (GC) determines when it needs to clean up the heap. If the program allocates and frees many reference objects, a lot of the heap may be full of memory that is no longer used. In that case, the garbage collector will decide to clean house.
When it runs, the garbage collector examines all the program’s reference variables, parameters that are object references, CPU registers, and other items that might point to heap objects. It uses those values to build a graph describing the heap memory that the program can still access. It then compacts the objects in the heap and updates the program’s references so they can find any moved items. The garbage collector then updates the heap itself so that the program can allocate memory from the unused portion.
When it destroys an object, the garbage collector frees the object’s memory and any managed resources it contains. It may not free unmanaged resources, however. You can determine when and how an object frees its managed and unmanaged resources by using the Finalize and Dispose methods.
When it destroys an object, the garbage collector frees any managed resources used by that object. For example, suppose that an unused object contains a reference to an open file stream. When the garbage collector runs, it notices that the file stream is inaccessible to the program, so it destroys the file stream, as well as the object that contains its reference.
However, suppose that the object uses an unmanaged resource that is outside of the scope of objects that Visual Basic understands. For example, suppose the object holds an integer representing a file handle, network connection, or channel to a hardware device that Visual Basic doesn’t understand. In that case, the garbage collector doesn’t know how to free that resource.
You can tell the garbage collector what to do by overriding the class’s Finalize method, which is inherited from the Object class. The garbage collector calls an object’s Finalize method before permanently removing the object from the heap. Note that there are no guarantees about exactly when the garbage collector calls this method, or the order in which different objects’ Finalize methods are called. Two objects’ Finalize methods may be called in either order even if one contains a reference to the other or if one was freed long before the other. If you must guarantee a specific order, you must provide more specific cleanup methods of your own.
Example program GarbageCollection uses the following code to demonstrate the Finalize method:
Public Class Form1
Public Running As Boolean
Private Class Junk
Public MyForm As Form1
Public Sub New(my_form As Form1)
MyForm = my_form
End Sub
' Garbage collection started.
Protected Overrides Sub Finalize()
' Stop making objects.
MyForm.Running = False
End Sub
End Class
' Make objects until garbage collection starts.
Private Sub btnCreateObjects_Click() Handles btnCreateObjects.Click
Running = True
Dim new_obj As Junk
Dim max_i As Long
For i As Long = 1 To 1000000
new_obj = New Junk(Me)
If Not Running Then
max_i = i
Exit For
End If
Next i
MessageBox.Show("Allocated " & max_i.ToString & " objects")
End Sub
End Class
The Form1 class defines the public variable Running. It then defines the Junk class, which contains a variable referring to the Form1 class. This class’s constructor saves a reference to the Form1 object that created it. Its Finalize method sets the Form1 object’s Running value to False.
When the user clicks the form’s Create Objects button, the btnCreateObjects_Click event handler sets Running to True and starts creating Junk objects, passing the constructor this form as a parameter. The routine keeps creating new objects as long as Running is True. Note that each time the routine creates a new object, the old object that the variable new_obj used to point to becomes inaccessible to the program so it is available for garbage collection.
Eventually the program’s heap runs low, so the garbage collector executes. When it destroys one of the Junk objects, the object’s Finalize subroutine executes and sets the form’s Running value to False. When the garbage collector finishes, the btnCreateObjects_Click event handler sees that Running is False, so it stops creating new Junk objects. It displays the number of the last Junk object it created and is done.
In one test, this program created 30,456 Junk objects before the garbage collector ran. In a second trial run immediately after the first, the program created 59,150 objects, and in a third it created 26,191. The garbage collector gives you little control over when it finalizes objects.
Visual Basic also calls every object’s Finalize method when the program ends. Again, there are no guarantees about the exact timing or order of the calls to different objects’ Finalize methods.
Example program FinalizeObjects, which is available for download on the book’s website, uses the following code to test the Finalize method when the program ends:
Public Class Form1
Private Class Numbered
Private Number As Integer
Public Sub New(my_number As Integer)
Number = my_number
End Sub
' Garbage collection started.
Protected Overrides Sub Finalize()
' Display the object's number.
Debug.WriteLine("Finalized object " & Number)
End Sub
End Class
' Make objects until garbage collection starts.
Private Sub btnGo_Click() Handles btnGo.Click
Static i As Integer = 0
i += 1
Dim new_numbered As New Numbered(i)
Debug.WriteLine("Created object " & i.ToString)
End Sub
End Class
The Numbered class contains a variable Number and initializes that value in its constructor. Its Finalize method writes the object’s number in the Output window.
The btnGo_Click event handler creates a new Numbered object, giving it a new number. When the event handler ends, the new_numbered variable referring to the Numbered object goes out of scope, so the object is no longer available to the program. If you look at the Output window at this time, you will probably find that the program has not bothered to finalize the object yet. If you click the button several times and then close the application, Visual Basic calls each object’s Finalize method. If you click the button five times, you should see five messages displayed by the objects’ Finalize methods.
If your class allocates unmanaged resources, you should give it a Finalize method to free them.
Because Visual Basic doesn’t keep track of whether an object is reachable at any given moment, it doesn’t know when it can permanently destroy an object until the program ends or the garbage collector reclaims it. That means the object’s memory and resources may remain unused for quite a while. The memory itself isn’t a big issue. If the program’s heap runs out of space, the garbage collector runs to reclaim some of the unused memory.
If the object contains a reference to a resource, however, that resource is not freed until the object is finalized, and that can have dire consequences. You generally don’t want control of a file, network connection, scanner, or other scarce system resource left to the whims of the garbage collector.
By convention, the Dispose subroutine frees an object’s resources. Before a program frees an object that contains important resources, it can call that object’s Dispose method to free the resources explicitly.
To handle the case where the program does not call Dispose, the class should also free any unmanaged resources that it holds in its Finalize subroutine. Because Finalize is executed whether or not the program calls Dispose, the class must also be able to execute both the Dispose and the Finalize subroutines without harm. For example, if the program shuts down some piece of unusual hardware, it probably should not shut down the device twice.
To make building a Dispose method a little easier, Visual Basic defines the IDisposable interface, which declares the Dispose method. If you enter the statement Implements IDisposable and press Enter, Visual Basic creates an empty Dispose method for you.
Example program UseDispose, which is available for download on the book’s website, uses the following code to demonstrate the Dispose and Finalize methods:
Public Class Form1
Private Class Named
Implements IDisposable
' Save our name.
Public Name As String
Public Sub New(new_name As String)
Name = new_name
End Sub
' Free resources.
Protected Overrides Sub Finalize()
Dispose()
End Sub
' Display our name.
Public Sub Dispose() Implements System.IDisposable.Dispose
Static done_before As Boolean = False
If done_before Then Exit Sub
done_before = True
Debug.WriteLine(Name)
End Sub
End Class
' Make an object and dispose it.
Private Sub btnDispose_Click() Handles btnDispose.Click
Static i As Integer = 0
i += 1
Dim obj As New Named("Dispose " & i)
obj.Dispose()
End Sub
' Make an object and do not dispose it.
Private Sub btnNoDispose_Click() Handles btnNoDispose.Click
Static i As Integer = 0
i += 1
Dim obj As New Named("No Dispose " & i)
End Sub
End Class
The Named class has a Name variable that contains a string identifying an object. Its Finalize method simply calls its Dispose method. Dispose uses a static variable named done_before to ensure that it performs its task only once. If it has not already run, the Dispose method displays the object’s name. In a real application, this method would free whatever resources the object holds. Whether the program explicitly calls Dispose, or whether the garbage collector calls the object’s Finalize method, this code is executed exactly once.
The main program has two buttons labeled Dispose and No Dispose. When you click the Dispose button, the btnDispose_Click event handler makes a Named object, giving it a new name, and then calls the object’s Dispose method, which immediately displays the object’s name.
When you click the No Dispose button, the btnNoDispose_Click event handler makes a new Named object with a new name and then ends without calling the object’s Dispose method. Later, when the garbage collector runs or when the program ends, the object’s Finalize method executes and calls Dispose, which displays the object’s name.
If your class allocates managed or unmanaged resources and you don’t want to wait for the garbage collector to get around to freeing them, you should implement a Dispose method and use it when you no longer need an object.