Select to view content in your preferred language

What is the best practise when using ComReleaser?

6817
15
Jump to solution
01-23-2013 06:05 AM
DuncanHornby
MVP Notable Contributor
Coding gurus,

I've got a question about best practise when using the ComReleaser object. In the first example below is some stub code showing how I would typically using ComReleaser when I'm processing within a single loop.

Using comRel As New ComReleaser()     pFeatureCursor = pFeatureClass.Update(pQueryFilter, False)   comRel.ManageLifetime(pFeatureCursor)   pFeature = pFeatureCursor.NextFeature    Do While Not pFeature Is Nothing    ' Do something  pFeatureCursor.UpdateFeature(pFeature)     pFeature = pFeatureCursor.NextFeature   Loop   pFeatureCursor.Flush() End Using


Now suppose I want to loop through another cursor whilst looping through the first cursor, so nested. Is it appropriate to have a Using statement within a using statement (1) or do you simply add the inner cursor to the comReleaser object (2). I ask as I've not seen any examples and was wondering what was the best approach when nesting cursors?

Example 1


Using comRel As New ComReleaser()     pFeatureCursor = pFeatureClass.Update(pQueryFilter, False)   comRel.ManageLifetime(pFeatureCursor)   pFeature = pFeatureCursor.NextFeature    Do While Not pFeature Is Nothing    ' Do something    Using comRel2 as New ComReleaser()       pFeatureCursor2 = pFeatureClass2.Search(pQueryFilter2, False)       comRel2.ManageLifetime(pFeatureCursor2)       ' Do something inner looping stuff  End Using    pFeatureCursor.UpdateFeature(pFeature)     pFeature = pFeatureCursor.NextFeature   Loop   pFeatureCursor.Flush() End Using


Example 2

Using comRel As New ComReleaser()     pFeatureCursor = pFeatureClass.Update(pQueryFilter, False)   comRel.ManageLifetime(pFeatureCursor)   pFeature = pFeatureCursor.NextFeature    Do While Not pFeature Is Nothing    ' Do something     pFeatureCursor2 = pFeatureClass2.Search(pQueryFilter2, False)       comRel.ManageLifetime(pFeatureCursor2)     ' Do something inner looping stuff    End Using    pFeatureCursor.UpdateFeature(pFeature)     pFeature = pFeatureCursor.NextFeature   Loop   pFeatureCursor.Flush() End Using


In example 2  pFeatureCursor2 is added to comRel on every cycle of the outer cursor loop, is that good, bad or irrelevant?
0 Kudos
15 Replies
JasonPike
Frequent Contributor
Same issue in Java.  In Java, we can call garbage collection methods, but garbage collection will run when it wants according to whatever algorithm is uses.


So you're saying that your code will not wait for garbage collection, why is that?  Because of the nested cursors it is running slow, or ?


Garbage collection will not run after each iteration of the loop, but each iteration of the loop opens a table. The tables need to be closed after each iteration because you can only have a limited number of tables open at one time. Garbage collection may not run until well after you leave the method that has the loop opening all the tables--it depends on things like memory pressure, generation size, etc.
0 Kudos
RichardFairhurst
MVP Alum
I wrote an Add-In that used a Windows form and many chained functions that create and release cursors using ReleaseComObject. Two of the functions use IDataStatistics, which takes a populated cursor as a parameter. When I was targeting ArcGIS 10.0 it all worked fine. Now with 10.1 I am getting a lot of errors about trying to access protected memory, which the MSDN help suggests is due to memory corruption, usually related to unmanaged code. The error help suggests that the memory corruption may be accumulating through many operations before triggering the error.

The failure seems to always happen on the second or third pass that is triggered by user form updates and occurs within the subroutines that have the cursor that feeds to an IDataStatistics object. The error either appears when I call the UniqueValues method of the IDataStatistics object or when I do a Search on the feature cursor before it is passed to the IDataStatistics object (only on the second or third time through the code, never the first). Its behavior can be altered by the use or lack of use of the ReleaseComObject statements for the cursor or the IDataStatistics. I have tried all of the combinations I can think of and orders of the releaser. Below is a sample of how the code is written with the two lines where the protected memory error can occur shown in Red. Any suggestions on what objects I should use the ReleaseComObject on and the order or placement of that code or how to trap these errors and issolate the real cause?

    Private Sub GetRoutesList()
        ' Set Up Query Filter for the Full Date Range of the Collision Layer respecting PDO and DUI flags
        Dim pQF As IQueryFilter = New QueryFilterClass
        pQF.WhereClause = "NOT " & fldCollisionDate & " IS NULL" & whereSeverity & whereDUI
        pQF.SubFields = fldCollisionDate & ", " & fldPrimaryRoad & ", " & fldRID & ", " & fldSeverity & ", party_sobriety_1, party_sobriety_2"

        ' Query Collisions Date Range and Street to fill Route Combobox List
        pQF.WhereClause = fldCollisionDate & " >= date '" & m_FromDate.ToString("yyyy-MM-dd HH:mm:ss") & "' AND " & fldCollisionDate & " <= date '" & m_ToDate.ToString("yyyy-MM-dd HH:mm:ss") & "' AND " & fldRID & " > ' ' AND Upper(" & fldPrimaryRoad & ") = '" & m_strPrimary & "'" & whereSeverity & whereDUI

        Dim dataStatisticsRoutes As IDataStatistics = New DataStatisticsClass
        Dim featCursorRouteList As IFeatureCursor = m_FLCollisions.Search(pQF, True)
        ' Use DataStatistics to get Unique Values
        dataStatisticsRoutes.Field = fldRID
        dataStatisticsRoutes.Cursor = featCursorRouteList
        Dim pEnum As System.Collections.IEnumerator = dataStatisticsRoutes.UniqueValues() 

        ' Fill the Combobox and suspend refresh until list is populated
        cboRoutes.BeginUpdate()
        ' Clear the Routes Combobox
        cboRoutes.Items.Clear()
        ' Reset the enumerator before trying to access its values
        pEnum.Reset()
        ' Read each Unique value and populate the Route list.  Sorting is handled by a Combobox property.
        For i As Int32 = 0 To dataStatisticsRoutes.UniqueValueCount - 1
            pEnum.MoveNext()
            cboRoutes.Items.Add(UCase(pEnum.Current()))
        Next i

        ' Release the QueryFilter
        If Not pQF Is Nothing Then
            System.Runtime.InteropServices.Marshal.ReleaseComObject(pQF)
        End If
        ' Release the dataStatistics
        If Not dataStatisticsRoutes Is Nothing Then
            System.Runtime.InteropServices.Marshal.ReleaseComObject(dataStatisticsRoutes)
        End If
        ' Release the Cursor
        If Not featCursorRouteList Is Nothing Then
            System.Runtime.InteropServices.Marshal.ReleaseComObject(featCursorRouteList)
        End If
        ' Allow the Combobox to refresh
        cboRoutes.EndUpdate()

        If cboRoutes.Items.Contains(m_strRoute) Then
            ' Set Combobox Text to the current Map Route
            cboRoutes.Text = m_strRoute
        ElseIf cboRoutes.Items.Count > 0 Then
            ' Set Combobox Text to First Route in the List
            cboRoutes.Text = cboRoutes.Items(0)
            m_strRoute = cboRoutes.Text
        End If
    End Sub


There are many other cursor calls that do not involve IDataStatistics and other Com objects from ArcGIS being created in the code. None of the cursors are embedded in each other and I have always used the ReleaseComObject call after I finish reading the cursor. These cursors have not generated errors so far, but I need to be sure they are not contributing to the problem. Any suggestions are appreciated.
0 Kudos
JasonPike
Frequent Contributor
I wrote an Add-In that used a Windows form and many chained functions that create and release cursors using ReleaseComObject. Two of the functions use IDataStatistics, which takes a populated cursor as a parameter. When I was targeting ArcGIS 10.0 it all worked fine. Now with 10.1 I am getting a lot of errors about trying to access protected memory, which the MSDN help suggests is due to memory corruption, usually related to unmanaged code. The error help suggests that the memory corruption may be accumulating through many operations before triggering the error. 

The failure seems to always happen on the second or third pass that is triggered by user form updates and occurs within the subroutines that have the cursor that feeds to an IDataStatistics object. The error either appears when I call the UniqueValues method of the IDataStatistics object or when I do a Search on the feature cursor before it is passed to the IDataStatistics object (only on the second or third time through the code, never the first). Its behavior can be altered by the use or lack of use of the ReleaseComObject statements for the cursor or the IDataStatistics. I have tried all of the combinations I can think of and orders of the releaser. Below is a sample of how the code is written with the two lines where the protected memory error can occur shown in Red. Any suggestions on what objects I should use the ReleaseComObject on and the order or placement of that code or how to trap these errors and issolate the real cause? 

    Private Sub GetRoutesList()
        ' Set Up Query Filter for the Full Date Range of the Collision Layer respecting PDO and DUI flags
        Dim pQF As IQueryFilter = New QueryFilterClass
        pQF.WhereClause = "NOT " & fldCollisionDate & " IS NULL" & whereSeverity & whereDUI
        pQF.SubFields = fldCollisionDate & ", " & fldPrimaryRoad & ", " & fldRID & ", " & fldSeverity & ", party_sobriety_1, party_sobriety_2"

        ' Query Collisions Date Range and Street to fill Route Combobox List
        pQF.WhereClause = fldCollisionDate & " >= date '" & m_FromDate.ToString("yyyy-MM-dd HH:mm:ss") & "' AND " & fldCollisionDate & " <= date '" & m_ToDate.ToString("yyyy-MM-dd HH:mm:ss") & "' AND " & fldRID & " > ' ' AND Upper(" & fldPrimaryRoad & ") = '" & m_strPrimary & "'" & whereSeverity & whereDUI

        Dim dataStatisticsRoutes As IDataStatistics = New DataStatisticsClass
        Dim featCursorRouteList As IFeatureCursor = m_FLCollisions.Search(pQF, True)
        ' Use DataStatistics to get Unique Values
        dataStatisticsRoutes.Field = fldRID
        dataStatisticsRoutes.Cursor = featCursorRouteList
        Dim pEnum As System.Collections.IEnumerator = dataStatisticsRoutes.UniqueValues() 

        ' Fill the Combobox and suspend refresh until list is populated
        cboRoutes.BeginUpdate()
        ' Clear the Routes Combobox
        cboRoutes.Items.Clear()
        ' Reset the enumerator before trying to access its values
        pEnum.Reset()
        ' Read each Unique value and populate the Route list.  Sorting is handled by a Combobox property.
        For i As Int32 = 0 To dataStatisticsRoutes.UniqueValueCount - 1
            pEnum.MoveNext()
            cboRoutes.Items.Add(UCase(pEnum.Current()))
        Next i

        ' Release the QueryFilter
        If Not pQF Is Nothing Then
            System.Runtime.InteropServices.Marshal.ReleaseComObject(pQF)
        End If
        ' Release the dataStatistics
        If Not dataStatisticsRoutes Is Nothing Then
            System.Runtime.InteropServices.Marshal.ReleaseComObject(dataStatisticsRoutes)
        End If
        ' Release the Cursor
        If Not featCursorRouteList Is Nothing Then
            System.Runtime.InteropServices.Marshal.ReleaseComObject(featCursorRouteList)
        End If
        ' Allow the Combobox to refresh
        cboRoutes.EndUpdate()

        If cboRoutes.Items.Contains(m_strRoute) Then
            ' Set Combobox Text to the current Map Route
            cboRoutes.Text = m_strRoute
        ElseIf cboRoutes.Items.Count > 0 Then
            ' Set Combobox Text to First Route in the List
            cboRoutes.Text = cboRoutes.Items(0)
            m_strRoute = cboRoutes.Text
        End If
    End Sub


There are many other cursor calls that do not involve IDataStatistics and other Com objects from ArcGIS being created in the code. None of the cursors are embedded in each other and I have always used the ReleaseComObject call after I finish reading the cursor. These cursors have not generated errors so far, but I need to be sure they are not contributing to the problem. Any suggestions are appreciated.


Richard,

It sounds like you are having problems similar to the ones described in this thread:
http://forums.arcgis.com/threads/75186-Memory-leak-with-FeatureClass-object

I've described several debugging techniques in the thread that you might find helpful. If anyone has tried using the WinDbg or Application Verifier, they haven't posted their results. I would start with WinDbg to see what calls are in the stack with the Search() and UniqueValues() call when they crash. If it happens to be the same underlying calls, it could really shed some light on what is going wrong. If you decide to try WinDbg, don't forget to set the _NT_SYMBOL_PATH environment variable so that the ArcObject calls are fully resolved.

At least one of the developers on the other thread has filed a support ticket with ESRI, so you might want to monitor that thread as well.

If you can narrow the problem down to a small stand-alone app, zip up the entire solution, and attach it to one of your posts, I'll be happy to spend some time debugging it.

Thanks,
Jason
0 Kudos
KenBuja
MVP Esteemed Contributor
Richard,

Did you see this thread that discovered the bug NIM087476  �??Memory leakage ESRI.ArcGIS.Geodatabase.IDataStatistics using UniqueValues property  with ArcGIS 10.1 with SP1�?�
0 Kudos
DuncanHornby
MVP Notable Contributor
Richard,

I was having ArcMap crash spectacularly and it was down to the IDatastatistics object as indicated by the thread that Ken has linked to. It took a few days of headbanging a brick wall and all manner of cursing before I discovered that thread so I re-engineered the code to use a cursor to obtain unique values. It is clearly a bug introduced into 10.1

Everyone else thanks for your advice on best practise when using comReleaser (which seems to be don't use it! 😄 )

Duncan
0 Kudos
RichardFairhurst
MVP Alum
Richard,

Did you see this thread that discovered the bug NIM087476  �??Memory leakage ESRI.ArcGIS.Geodatabase.IDataStatistics using UniqueValues property  with ArcGIS 10.1 with SP1�?�


Thanks for pointing that out.  I had not seeen this thread, but this explains why it worked at 10.0 and not at 10.1 SP1 (I skipped 10.1 original).  I will reengineer the code to use a cursor to get unique values.  Can anyone pioint me to any threads that show the most elegant techniques for doing that?
0 Kudos