Handling Memory and Garbage The common language runtime handles all memory allocation and management that your application requires. This includes the initial allocation that occurs when you declare an object and store data in it, and the release of memory back to the operating system when the object is no longer in use. The automatic garbage collection of unused objects solves all the inherent problems of Win32-based applications when it comes to the mysterious resource losses that Windows would succumb to after running applications.Memory management is improved with each new version of the Windows operating system, but the fault is not completely that of the operating system. If you're writing C++ applications, it's very easy to forget to destroy object handles and memory blocks after they've been used. When this happens, there's no way for Windows to recover that memory until the machine is rebooted. In Visual Basic 6, you had to set all your object instances to Nothing to guarantee that memory would be freed after an object was used. The limitations for the runtime environments of all languages lead to the problems of resource loss in Windows. So, in the end, it isn't really the fault of Windowsit's the fault of the developers writing the code that runs in Windows.The garbage collection mechanism used in .NET is very simple and can be summed up in the following steps:
- The garbage collector (GC) allocates memory resources on the managed heap when a process starts and sets a pointer to that memory allocation.
- Each object created for the allocated resource is given the next address space in the managed heap when it's created.
- The GC continuously scans the managed heap for objects that are out of scope and no longer in use.
- The GC reclaims stack heaps that it determines are out of scope and compacts the managed heap for the running process.
This four-step process occurs over and over during the execution lifetime of your application. Under the hood, the GC divides the managed heap running your processes into three generations. Each generation is examined separately by the GC based on when the objects on the heap were created and their dependency to each other. This mechanism improves the overall performance of garbage collection because constantly scanning the entire managed heap for unused resources would be processor-intensive and time-consuming. By splitting apart when and where objects are created, the process of garbage collection can effectively determine what objects are in use and what objects are out of scope.Although the GC can handle the destruction of most objects on the managed heap, objects such as file handles, network handles, database connections, and window handles are unmanaged resources that are created on the managed heap. These resources can be given the correct memory allocation, and the GC knows when they are out of scope, but it doesn't know when to destroy those objects to reclaim the memory on the stack. To reclaim memory from unmanaged resources, you must explicitly destroy the objects by creating the necessary cleanup code to implement the IDisposable interface and override the Dispose method of the object. This isn't always necessary, and should be used only if you know that a resource must be freed when your component is no longer being used.If you're using an object and you know it's a CTS-compliant managed type, the automatic garbage collection handles reclaiming the resource. Haphazardly calling the Dispose method on objects consumes resources and forces garbage collection. When writing components that use unmanaged resources, you can close file handles and network handles in the Dispose method, and the normal process of garbage collection destroys the object and reclaims the memory allocation.NoteBecause the common language runtime determines when garbage collection takes place, it's referred to as nondeterministic finalization. In other words, you have no idea when the finalize method, which marks an object for collection, will occur.The reason that understanding the existence of Dispose method is important is because of an unlikely worst-case scenario in which object resources aren't freed and a component attempts to create them again. This situation could occur if the system running a component is depleting its resources and garbage collection isn't occurring on a regular basis. The following code demonstrates how to implement the Dispose method when creating a Windows User Control and implementing a database connection. Imports System.Data.SqlClient Public Class UserControl1 Inherits System.Windows.Forms.UserControl Private cn As New SqlConnection() Public Sub New() MyBase.New() cn.ConnectionString = "uid=sa;pwd=;database=pubs;server=." cn.Open() InitializeComponent() End Sub Protected Overloads Overrides Sub Dispose(ByVal disposing As Boolean) If disposing Then If Not (components Is Nothing) Then components.Dispose() cn.Close() cn = Nothing End If End If MyBase.Dispose(disposing) End Sub Private components As System.ComponentModel.IContainer Private Sub InitializeComponent() Me.Name = "UserControl1" End Sub End Class
 using System; using System.Collections; using System.ComponentModel; using System.Drawing; using System.Data; using System.Windows.Forms; using System.Data.SqlClient; namespace cSharpDispose { public class UserControl1 : System.Windows.Forms.UserControl { private System.ComponentModel.Container components = null; private SqlConnection cn; public UserControl1() { InitializeComponent(); cn.ConnectionString= "database=pubs;server=localhost;uid=sa;pwd=;"; cn.Open(); } protected override void Dispose( bool disposing ) { if( disposing ) { if( components != null ) components.Dispose(); cn.Close(); cn=null; } base.Dispose( disposing ); } private void InitializeComponent() { this.Name = "UserControl1"; this.Load += new System.EventHandler(this.UserControl1_Load); } } }
As you can see, implementing Dispose is a simple task. By default, any class that derives from System.ComponentModel.Component has a Dispose method that you can override. If you're writing a component that doesn't derive from System.ComponentModel.Component, you can implement the IDisposable interface and create your own Dispose method.
|