How to release COM references


This document was published with and applies to ArcGIS 9.3.
A 10 version also exists. A 9.2 version also exists.
Summary This topic provides information on the interoperation of Component Object Model (COM) and .NET including how memory is managed in the two different models.

Development licensing Deployment licensing
ArcView
ArcEditor
ArcInfo
Engine Developer Kit

In this topic


AOUninitialize.Shutdown

Unexpected errors can occur when a stand-alone application attempts to shut down. For example, you may have experienced errors on exit from an ArcGIS Engine application hosting a MapControl with a loaded map document, with an error message similar to The instruction x references memory at x. The memory could not be read. Such an error will be outside the scope of any error handling statements in your code.
 
These errors can often result when COM objects remain in memory longer than expected, preventing the correct unloading of the COM libraries from the process when it shuts down. To help stop such errors, a static Shutdown function has been added to the ESRI.ArcGIS.ADF assembly. This function can help avoid such errors by ensuring that COM references no longer in use are unloaded prior to the process shutdown.
 
The following code example shows how you can use this function in the Disposed method of a form:
 

[VB.NET]
Private Sub Form1_Disposed(ByVal sender As Object, ByVal e As System.EventArgs) Handles MyBase.Disposed
    ESRI.ArcGIS.ADF.COMSupport.AOUninitialize.Shutdown()
End Sub

[C#]
private void Form1_Disposed(object sender, System.EventArgs e)
{
  ESRI.ArcGIS.ADF.COMSupport.AOUninitialize.Shutdown();
}
This function can only help with unloading libraries for which no COM objects still exist; it is most useful to apply this function after any COM objects have been disposed of. For example, in an ArcGIS Engine windows application with a startup form, it is useful to place your call to AOUninitialize.Shutdown in the Form Disposed event handler.
 

ComReleaser class

The AOUninitialize.Shutdown function handles many of the shutdown problems in stand-alone applications, particularly relating to applications containing the Engine Controls. However, you may still experience problems where COM objects remain in memory in your application and need to be explicitly released from memory.
 
To ensure a COM object is released when it goes out of scope, you can use the ComReleaser class. The ComReleaser class can be found in the ESRI.ArcGIS.ADF.dll assembly. You may want to use this class to keep track of your COM objects, as it can help ensure your object references are disposed of when your code terminates. See the ESRI.ArcGIS.ADF library reference documentation for more information on using this class.
 
Internally, this class uses the ReleaseCOMObject method on the System.Runtime.InteropServices.Marshal class to ensure COM object references are terminated. It is important to note that this should only be called on objects that are no longer required in any managed code in the process. See the following Marshal.ReleaseComObject section and the Microsoft Developer Network (MSDN) documentation on ReleaseComObject for more information.
 

Marshal.ReleaseComObject

In .NET code, references to COM objects are held via runtime callable wrappers (RCWs), managed objects that act as proxy objects for the underlying COM object. The .NET garbage collector is responsible for clearing managed objects from memory, which happens in a nondeterministic way. In a case where a COM object holds system resources (file handles, database connections, and so on), you may need to explicitly release certain COM objects to free the resources held by the COM object.
 
Such problems can cause different types of errors depending on the circumstances. For example, repeatedly opening geodatabase cursors against a personal geodatabase without ensuring the last cursor was freed may cause an error indicating that no more tables can be opened. In other situations, error messages may occur on application shutdown, as object references remain in memory; the StyleGallery is one such class that can often cause errors on exit if you do not explicitly release it.
 
To fully free the COM object underlying an RCW from memory at a deterministic point, it is possible to use the ReleaseComObject method on the Marshal class, which is part of the System.Runtime.InteropServices namespace in the .NET Framework. Calling ReleaseComObject will decrease the reference count held on an RCW; once the reference count on the RCW reaches zero (which may require repeated calls to ReleaseComObject), the RCW is marked for garbage collection. If no other COM objects hold a reference to the underlying COM object at that point, the COM runtime will also clear up the COM object itself.
 
Calling ReleaseComObject affects all managed references to a COM object in the current process. You should be particularly careful when calling this method from an in-process component, such as a dynamic-link library (DLL), which is loaded into an ArcGIS Desktop application. It is not recommended that you call ReleaseComObject on any object to which another managed component may have a reference. For example, if you have stored a reference to MxDocument and call ReleaseComObject on that reference, then any other managed in-process component is unable to access the MxDocument object from that point on. If you are creating a stand-alone application and have the advantage of controlling all the managed code in that application, you are able to determine more precisely when you can release the COM objects.
The following example code shows how you can call ReleaseComObject to release a StyleGallery object. The code shown recursively calls ReleaseComObject until the returned value is zero. This indicates there are no longer any managed references to the StyleGallery, and such code should only be used when you are sure no other managed code will require further access to the object.
 

[VB.NET]
Sub Main()
    Dim styCls As ESRI.ArcGIS.Display.IStyleGallery = New ESRI.ArcGIS.Framework.StyleGalleryClass
    ' Use the StyleGalleryClass here.
    Dim refsLeft As Integer = 0
    Do
        refsLeft = System.Runtime.InteropServices.Marshal.ReleaseComObject(styCls)
    Loop While (refsLeft > 0)
End Sub

[C#]
private void MyFunction()
{
  ESRI.ArcGIS.Display.IStyleGallery styCls = new
    ESRI.ArcGIS.Framework.StyleGalleryClass()as
    ESRI.ArcGIS.Display.IStyleGallery;
  // Use the StyleGalleryClass here.
  int refsLeft = 0;
  do
  {
    refsLeft = Marshal.ReleaseComObject(styCls);
  }
  while (refsLeft > 0);
}
You may also want to call ReleaseComObject on objects that you create in a loop (such as enumerators), because you can be sure the objects are freed in a timely manner, rather than waiting for the garbage collection process to perform the cleanup. This is useful when dealing with cursor objects and other geodatabase objects that hold resources as well as style gallery enumerators and item objects. See the following Releasing geodatabase cursors section for more information.
 
For more information about the garbage collection process and the ReleaseComObject method, refer to MSDN documentation.
 

Releasing geodatabase cursors

Some objects may lock or use resources that the object frees only in its destructor. For example, in the ESRI libraries, a geodatabase cursor can acquire a shared schema lock on a file-based feature class or table on which it is based or can hold on to a Spatial Database Engine (SDE) stream.
 
While the shared schema lock is in place, other applications can continue to query or update the rows in the table, but they cannot delete the feature class or modify its schema. In the case of file-based data sources, such as shapefiles, update cursors acquire an exclusive write lock on the file, which prevents other applications from accessing the file for read or write purposes. The effect of these locks is that the data may be unavailable to other applications until all the references on the cursor object are released.
 
In the case of SDE data sources, the cursor holds on to an SDE stream, and if the application has multiple clients, each may get and hold on to an SDE stream, eventually exhausting the maximum allowable streams. The effect of the number of SDE streams exceeding the maximum is that other clients will fail to open their own cursors to query the database. In other cases, the server may exhaust its available memory.
 
In .NET, your reference on the cursor (or any other COM object) will not be released until garbage collection occurs; only then will the resources be released. Therefore, if you are performing a number of operations using objects that lock database resources, you may find that these resources can accumulate up to the maximum allowable limits for the data source. At this point, errors can vary depending on that data source; they may indicate the lack of resources. For example, accessing personal geodatabase tables may indicate only 255 connections to tables are allowed. In other cases, the exceptions may not be obviously related to the lack of resources.
 
Calling GC.Collect, the .NET method to initiate garbage collection at a known point, can be used to clear any pending objects. However, forcing garbage collection is not generally recommended, as the call itself can be slow and forcing the collection can also interfere with optimum garbage collector operation. The correct approach is to free objects that may hold resources by using Marshal.ReleaseComObject. Typically, you should always release cursor objects in this way (for example, objects implementing IFeatureCursor). You may also want to release objects implementing ISet as well as geodatabase enumerators. Again, you should not free any object that needs to be accessed from elsewhere within .NET code; for example, objects that are long lived that you did not create in your own code.
 
In a Web application or Web service servicing multiple concurrent sessions and requests, relying on garbage collection to release references on objects holding resources will result in cursors and their resources not being released in a timely manner. Similarly, in an ArcGIS Desktop or Engine application, relying on garbage collection to release such references can result in your application or other applications receiving errors.
 

ArcGIS Desktop and Engine

If you are using geodatabase cursors in your code, you may want to call Marshal.ReleaseComObject on each cursor object when you no longer require it to ensure any geodatabase resources are released in a timely manner. The following code example demonstrates this pattern:
 

[VB.NET]
For i As Integer = 1 To 2500
    Dim qu As IQueryFilter = New QueryFilterClass
    qu.WhereClause = "Area = " & i.ToString()
    Dim featCursor As IFeatureCursor = featClass.Search(qu, True)
    ' Use the feature cursor as required.
    System.Runtime.InteropServices.Marshal.ReleaseComObject(featCursor)
Next i

[C#]
for (int i = 1; i < 2500; i++)
{
  IQueryFilter qu = New QueryFilterClass();
  qu.WhereClause = @"Area = " + i.ToString();
  IFeatureCursor featCursor = featClass.Search(qu, true);
  // Use the feature cursor as required.
  System.Runtime.InteropServices.Marshal.ReleaseComObject(featCursor);
}
ArcGIS Engine
The ComReleaser class plays an important role in working with singletons in ArcGIS Engine. As mentioned previously, COM objects left in memory for an extended period after use may cause problems and errors on shutdown. Singletons are a type of COM object that have only one instance per process; you may find your code results in errors if singletons are left hanging (a process known as pinning). In the case of singletons, it is necessary to always unpin (or release) the reference, regardless of the specific API you are using. For non-.NET APIs, simply set the member variable equal to nothing. For the .NET API, release the singleton COM object using the ComReleaser class as specified in the previous ComReleaser Class section. For more information on specific singletons used in ArcGIS Engine, see Using the control commands: Singleton objects.

ArcGIS Server

To ensure a COM object is released when it goes out of scope, the WebControls assembly contains a helper object called WebObject. Use the ManageLifetime method to add your COM object to the set of objects that will be explicitly released when the WebObject is disposed of. You must scope the use of WebObject within a using block so that any object (including your cursor) that you have added to the WebObject using the ManageLifetime method will be explicitly released at the end of the using block.
 
The following code example demonstrates this pattern:
 

[VB.NET]
Private SubSystem.Object doSomething_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles doSomething.Click
    Dim webobj As WebObject = New WebObject
    Dim ctx As IServerContext = Nothing
    Try
    Dim serverConn As ServerConnection = New ServerConnection("doug", True)
    Dim som As IServerObjectManager = serverConn.ServerObjectManager
    ctx = som.CreateServerContext("Yellowstone", "MapServer")
    Dim mapsrv As IMapServer = ctx.ServerObject
    Dim mapo As IMapServerObjects = mapsrv
    Dim map As IMap = mapo.Map(mapsrv.DefaultMapName)
    Dim flayer As IFeatureLayer = map.Layer(0)
    Dim fClass As IFeatureClass = flayer.FeatureClass
    Dim fcursor As IFeatureCursor = fClass.Search(Nothing, True)
    webobj.ManageLifetime(fcursor)
    Dim f As IFeature = fcursor.NextFeature()
    Do Until f Is Nothing
        ' Do something with the feature.
        f = fcursor.NextFeature()
    Loop
    Finally
    ctx.ReleaseContext()
    webobj.Dispose()
    End Try
End Sub

[C#]
private void doSomthing_Click(object sender, System.EventArgs e)
{
  using(WebObject webobj = new WebObject())
  {
    ServerConnection serverConn = new ServerConnection("doug", true);
    IServerObjectManager som = serverConn.ServerObjectManager;
    IServerContext ctx = som.CreateServerContext("Yellowstone", "MapServer");
    IMapServer mapsrv = ctx.ServerObject as IMapServer;
    IMapServerObjects mapo = mapsrv as IMapServerObjects;
    IMap map = mapo.get_Map(mapsrv.DefaultMapName);
    IFeatureLayer flayer = map.get_Layer(0)as IFeatureLayer;
    IFeatureClass fclass = flayer.FeatureClass;
    IFeatureCursor fcursor = fclass.Search(null, true);
    webobj.ManageLifetime(fcursor);
    IFeature f = null;
    while ((f = fcursor.NextFeature()) != null)
    {
      // Do something with the feature.
    }
    ctx.ReleaseContext();
  }
}
The WebMap, WebGeocode, and WebPageLayout objects also have a ManageLifetime method. If you are using, for example, a WebMap and scope your code in a using block, you can rely on these objects to explicitly release objects you add with ManageLifetime at the end of the using block.


See Also:

Writing multithreaded ArcObjects code
Singleton objects