Releasing COM references


Summary This topic provides information on the interoperation of the Component Object Model (COM) and .NET including how memory is managed in the two different models.

In this topic


AOUninitialize.Shutdown

Unexpected errors can occur when a stand-alone application attempts to shut down. For example, you might have received errors similar to the following when exiting from an ArcGIS Engine application that hosts a MapControl with a loaded map document (this type of error is outside the scope of any error handling statements in your code):
  • The instruction x references memory at x. The memory could not be read.
These errors can occur 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 prevent these errors, a static Shutdown function has been added to the ESRI.ArcGIS.ADF.Local assembly. This function helps to avoid these errors by ensuring that COM references that are not in use are unloaded before the process shuts down.
The following code example shows how you can use this function in the Disposed method for 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 only helps with unloading libraries for which no COM objects still exist. It is more beneficial to apply this function after any COM objects have been disposed of. For example, in an ArcGIS Engine windows application with a startup form, 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 can 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, use the ComReleaser class. The ComReleaser class is located in the ESRI.ArcGIS.ADF.Connection.Local.dll assembly. You can use this class to manage COM objects, as it helps to ensure your object references are disposed of when your code terminates. For more information on using this class, see ESRI.ArcGIS.ADF.Connection.Local.
Internally, this class uses the ReleaseCOMObject method on the System.Runtime.InteropServices.Marshal class to ensure COM object references are terminated. It is important that this is only called on objects that are not required in any managed code in the process. For more information, see the Marshal.ReleaseComObject section in this topic and Marshal.ReleaseComObject Method on the Microsoft Developer Network (MSDN) Web site.

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 might need to explicitly release certain COM objects to free the resources held by the COM object.
These 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 can cause an error indicating that no more tables can be opened. In other situations, error messages can occur on application shutdown, as object references remain in memory. The StyleGallery is this type of class that can 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 decreases the reference count held on an RCW. Once the reference count on the RCW reaches zero (which can 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 also clears up the COM object.
Calling ReleaseComObject affects all managed references to a COM object in the current process. 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. Do not call ReleaseComObject on any object to which another managed component might have a reference. For example, if you have stored a reference to MxDocument and call ReleaseComObject on that reference, any other managed in-process component is unable to access the MxDocument object from that point. 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 code example shows how you can call ReleaseComObject to release a StyleGallery object. The code recursively calls ReleaseComObject until the returned value is zero. This indicates there are no longer any managed references to the StyleGallery, and this type of code should only be used when you are sure no other managed code requires 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 can also 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. For more information, see the Releasing geodatabase cursors section in this topic.
For more information about the garbage collection process, see Garbage Collection on the MSDN Web site.

Releasing geodatabase cursors

Some objects can 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 can 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 can 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 fail to open their cursors to query the database. In other cases, the server can 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 might 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 can indicate the lack of resources. For example, accessing personal geodatabase tables can indicate only 255 connections to tables are allowed. In other cases, the exceptions might 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, do not force garbage collection, as the call can be slow and forcing the collection can also interfere with optimum garbage collector operations. The correct approach is to free objects that can hold resources by using Marshal.ReleaseComObject. Typically, you should always release cursor objects in this way (for example, objects implementing IFeatureCursor). You might 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 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 result in cursors and their resources not being released in a timely manner. Similarly, in an ArcGIS Desktop or ArcGIS Engine application, relying on garbage collection to release these references can result in your application or other applications receiving errors.

ArcGIS Desktop and ArcGIS Engine

If you are using geodatabase cursors in your code, you can 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 previously mentioned, COM objects left in memory for an extended period after use can cause problems and errors on shutdown. Singletons are a type of COM object that have only one instance per process. You might 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 application programming interface (API) you are using. For non-.NET APIs, 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 the "Singleton objects" section in Using the control commands.

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
Interacting with singleton objects




Development licensing Deployment licensing
ArcView
ArcEditor
ArcInfo
Engine Developer Kit