Memory Leak with Graphic Tracker

3157
15
11-29-2012 11:49 PM
JeremieJoalland1
Occasional Contributor II
I'm working on application which will never shutdown... the main interface is containing a MapControl control, in order for the application to dynamicaly display on the map data received from other applications.
We are developing with ArcGIS Engine 10.0 SP4, C#.net and Visual Studio 2010.
We use GraphicTracker (4 to manage display order) to draw geometries (points and curve polylines).

We currently face a Memory Leak for this application when doing stress tests : we send data to the application every second or 2 seconds, each time the application must clean the Graphic Tracker, release all COM Objects and draw again new data on the Graphic Tracker. after 2 or 3 hours, we reach an OutOfMemory Exception or -2147467259 runtime error... But our application is a 24/24 hours system and should never stop, neither restart !

I guess, my release objects methods are wrong, but can't find a solution...

So could somebody look to my code for any solutions or ideas ?

Form1 = simple Form with MapControl + 2 buttons to start/stop a 1 second Timer which send clean/drawing order for 10000
public partial class Form1 : Form
    {
        ...

        private void Form1_Load(object sender, EventArgs e)
        {
            // Load on MXD map (sample : 1 shapefile of world countries)
                this.LoadMyMXDMap();

                // Initialize the Timer
                this.InitializeMyTimer();
        }

        private void BtnStartMyTimer_Click(object sender, EventArgs e)
        {
            timer1.Start();
        }

        ...

        private void InitializeMyTimer()
        {
            timer1.Interval = 1000;
        }

        private void Timer1_Tick(object sender, EventArgs e)
        {
                // Stop the Timer before drawing
                timer1.Stop();

                // Use the Graphic Tracker Controler to perform a new drawing of 10000 points
                if (this.graphicTrackerControler != null)
                {
                    // Try a clean dispose & release of all COM objects
                    // MEMORY LEAK must come from this Method ??
                    this.graphicTrackerControler.DisposeGraphicTracker();
                }

                // Initialize new Graphic Tracker Controler and necessary objects
                this.graphicTrackerControler = new GraphicsControler();
                this.graphicTrackerControler.InitializeGraphicTracker();

                // Draw on Graphic Trackers 10000 point's geometries
                this.graphicTrackerControler.DrawPointsOnGraphicTracker();

                // Restart the Timer after drawing
                timer1.Start();
        }

        private void LoadMyMXDMap()
        {
                // my MXD doc map
                string mxdFilePath = @"C:\my_maps\WORLD.mxd";

                IMapDocument mapDocument = new MapDocumentClass();
                mapDocument.Open(mxdFilePath);

                // Display the map on Map Control
                Form1.CurrentMap = mapDocument.get_Map(0);
                this.axMapControl.Map = Form1.CurrentMap;
        }
    }


one Class "GraphicsControler" which manage the Graphic Tracker (initialize, dispose, draw) and added geometries
    public class GraphicsControler
    {
        /// <summary> Graphic Tracker
        /// </summary>
        private IGraphicTracker graphicTrackerLayer;

        /// <summary> List of Geometries added to the Graphic Tracker
        /// </summary>
        private List<IGeometry> listOfGeometries;

        /// <summary> Symbol for Point's geometries on the Graphic Tracker
        /// </summary>
        private IGraphicTrackerSymbol symbolForPoints;

        public void DrawPointsOnGraphicTracker()
        {
            try
            {
                if (this.graphicTrackerLayer != null)
                {
                    // Disable graphic tracker auto-refresh
                    this.graphicTrackerLayer.SuspendUpdate = true;

                    IGeometry geometry;
                    IPoint point;
                    double coordX = 500;
                    double coordY = 5000;

                    // Add 10000 point geometries to the Graphic Tracker
                    for (int i = 0; i < 10000; i++)
                    {
                        coordX = coordX + 500;
                        coordY = coordY + 600;

                        point = new PointClass();
                        point.PutCoords(coordX, coordY);
                        geometry = point as IGeometry;

                        // Add the geometry to the graphic tracker
                        this.graphicTrackerLayer.Add(geometry, this.symbolForPoints);

                        // Preserve the added geometry for future COM release
                        this.listOfGeometries.Add(geometry);
                    }

                    // Enable graphic tracker auto-refresh
                    this.graphicTrackerLayer.SuspendUpdate = false;
                }
            }
            catch (Exception exc)
            {
                MessageBox.Show(exc.Message, "DrawPointsOnGraphicTracker");
            }
        }

        public void InitializeGraphicTracker()
        {
            try
            {
                this.graphicTrackerLayer = new GraphicTrackerClass();
                this.graphicTrackerLayer.Initialize(Form1.CurrentMap as IBasicMap);

                // Create one Symbol for the added points
                this.CreateGraphicTrackerSymbolForPoints();

                this.listOfGeometries = new List<IGeometry>();
            }
            catch (Exception exc)
            {
                MessageBox.Show(exc.Message, "InitializeGraphicTracker");
            }
        }

        public void DisposeGraphicTracker()
        {
            try
            {
                // MEMORY LEAK must come from this Method ??
                if (this.listOfGeometries != null)
                {
                    if (this.listOfGeometries.Count > 0)
                    {
                        // Release COM for each Geometry added to the Graphic Tracker
                        foreach (IGeometry item in this.listOfGeometries)
                        {
                            item.SetEmpty();
                            this.CompleteReleaseOfCOMObject(item);
                        }

                        this.listOfGeometries.Clear();
                    }
                }

                if (this.graphicTrackerLayer != null)
                {
                    // Disable graphic tracker auto-refresh
                    this.graphicTrackerLayer.SuspendUpdate = true;
                                        
                    // Delete all geometries added to the Graphic Tracker
                    //  this method seams to clean the Graphic Tracker, without releasing memory
                    this.graphicTrackerLayer.RemoveAll();

                    // Enable graphic tracker auto-refresh
                    this.graphicTrackerLayer.SuspendUpdate = false;
                }

                // Release my Symbol
                this.CompleteReleaseOfCOMObject(this.symbolForPoints);

                // Release my Graphic Tracker
                this.CompleteReleaseOfCOMObject(this.graphicTrackerLayer);
            }
            catch (Exception exc)
            {
                MessageBox.Show(exc.Message, "DisposeGraphicTracker");
            }
        }

        private void CreateGraphicTrackerSymbolForPoints()
        {
            try
            {
                if (this.graphicTrackerLayer != null)
                {
                    ICharacterMarkerSymbol characterMarkerSymbol;
                    stdole.IFontDisp symbolFont = new stdole.StdFontClass() as stdole.IFontDisp;
                    symbolFont.Name = "ESRI Default Marker";

                    characterMarkerSymbol = new CharacterMarkerSymbolClass();
                    characterMarkerSymbol.Font = symbolFont;
                    characterMarkerSymbol.Color = new RgbColorClass() { Red = 0, Green = 0, Blue = 0 };
                    characterMarkerSymbol.CharacterIndex = 86;
                    characterMarkerSymbol.Size = 10;

                    this.symbolForPoints = this.graphicTrackerLayer.CreateSymbol(characterMarkerSymbol as ISymbol, null);
                }
            }
            catch (Exception exc)
            {
                MessageBox.Show(exc.Message, "CreateGraphicTrackerSymbolForPoints");
            }
        }

        private void CompleteReleaseOfCOMObject(object objetALiberer)
        {
            try
            {
                ComReleaser.ReleaseCOMObject(objetALiberer);

                if (objetALiberer != null)
                {
                    int refsLeft = 0;
                    do
                    {
                        refsLeft = Marshal.ReleaseComObject(objetALiberer);
                    }
                    while (refsLeft > 0);
                }
            }
            catch (Exception exc)
            {
                MessageBox.Show(exc.Message, "CompleteReleaseOfCOMObject");
            }
        }
    }


Thanks in advance for any help !

complete source code in .zip is attached.
0 Kudos
15 Replies
BillLanza
New Contributor
Was wondering if you made any progress on this. I am having similar issues.
Thanks
0 Kudos
JasonPike
Occasional Contributor
I'm working on application which will never shutdown... the main interface is containing a MapControl control, in order for the application to dynamicaly display on the map data received from other applications.
We are developing with ArcGIS Engine 10.0 SP4, C#.net and Visual Studio 2010.
We use GraphicTracker (4 to manage display order) to draw geometries (points and curve polylines).
<snip>


There is a lot going on here so I just made edits and attached them to the post. Here are some notes though:

Check out this thread, which explains some basics of COM interop:
http://social.msdn.microsoft.com/Forums/en-US/clr/thread/3090e8ec-fde0-4159-9038-8ed58eccbf96

I know I made dozens of changes, so feel free to ask questions on specifics if you have any. I only attached the files that I changed and I added comments here and there. Sorry for changing things that didn't necessarily need it--that is how I organize my thoughts sometimes.

Please let me know if the example works with the changes I made. If not, I'll make a second pass.
0 Kudos
JeremieJoalland1
Occasional Contributor II
I've tried you code, but it doesn't seem to change the memory usage.... there is still memory leak.

Since my first post, i've tried to clean all my ArcGIS COM Object properly with Marshal.ReleaseCOMObject() as it is non-managed code for .Net but without success.

in fact, there is no memory leak if we comment the IGraphicTracker::Add() method. That is why I think there is a problem with the IGraphicTracker::Remove() method...  in comparison, if we do a similar program with IGraphicsContainer instead, there is no memory leak, as the IGraphicsContainer::DeleteAllElements() method seems to clean all memory.

The main issue with IGraphicTracker is that once you have added you graphic, there is no way to get it, as there is no enumerator on this object... so I can't find a way around its Remove() method, which is not cleaning memory...

I'm expecting answers from ESRI technical support on this subject, and will let you know.
0 Kudos
LeoDonahue
Occasional Contributor III

The main issue with IGraphicTracker is that once you have added you graphic, there is no way to get it, as there is no enumerator on this object... so I can't find a way around its Remove() method, which is not cleaning memory...

When you call the add method on GraphicTracker, it returns an integer used to track the graphic.  Are you storing this integer value somewhere?  You need it in order to call remove (because it takes an int id).

You could also consider using GraphicElements and give each a unique name. Then you can iterate over your graphic elements and find the element you want by name.
0 Kudos
JasonPike
Occasional Contributor
When you call the add method on GraphicTracker, it returns an integer used to track the graphic.  Are you storing this integer value somewhere?  You need it in order to call remove (because it takes an int id).

You could also consider using GraphicElements and give each a unique name. Then you can iterate over your graphic elements and find the element you want by name.


Storing the IDs and removing them one by one has the same problem that calling RemoveAll does: even when the .NET RCWs are released properly, the underlying COM objects are not released until IAoInitialize.Shutdown() is called. I have also tried releasing and recreating the graphic tracker with the same resulting memory leak.

I have attached an example that is a greatly simplified version of the original example (it also includes removing the geometries one by one using the Remove(int id) method you suggested.) It reproduces the problem described in the original post. I have verified that every RCW is released properly, so I have to agree with Jeremie that the problem is ESRI's.

Thanks,
Jason
0 Kudos
LeoDonahue
Occasional Contributor III
What if you kill off the IGeometry that you used to add to the GraphicsTracker after you add it?
0 Kudos
JasonPike
Occasional Contributor
What if you kill off the IGeometry that you used to add to the GraphicsTracker after you add it?


The example I've attached above calls Marshal.ReleaseComObject on each IGeometry instance after it is added to the GraphicsTracker. Unfortunately, the results are the same.
0 Kudos
JeremieJoalland1
Occasional Contributor II
Happy New Year !

I just want to confirm that I did all same tests that ScJpike, with RemoveAll() or Remove(), and try to releaseCom all objects added to the Graphic Tracker... but still with the same memory leak result. I've stop my investigation by now, and still expecting some news from ESRI support.
0 Kudos
JasonPike
Occasional Contributor
For those who are interested, below is output from UMDH that shows the memory that is still allocated after RemoveAll() is called and the stacks that allocated the memory. Between markings, I added 10,000 points to a GraphicTracker instance and called RemoveAll(). It appears that a new GraphicTracker is created each time a point is added (see the 9999x that GraphicTracker is created, which is 1x per point added minus the instance that was created before the first UMDH mark.) It also appears that each Point that is added is cloned twice! Neither Clone appears to be freed when RemoveAll() is called. If my interpretation is incorrect, I welcome correction...

+ 1520000 ( 1520000 -      0)  10000 allocs BackTrace106BF720
+   10000 (  10000 -      0) BackTrace106BF720 allocations

ntdll!RtlAllocateHeap+00000274
MSVCR90!malloc+00000079
MSVCR90!operator new+0000001F
Geometry!ESRI::Point::Clone+0000000D
EngineGraphics!GraphicTracker::AddWithOptions+0000004D
EngineGraphics!GraphicTracker::Add+00000051
mscorwks!CLRToCOMWorker+0000019A
<no module>!???+00000000 : 3CA70A

+ 1520000 ( 1520000 -      0)  10000 allocs BackTraceB522378
+   10000 (  10000 -      0) BackTraceB522378 allocations

ntdll!RtlAllocateHeap+00000274
MSVCR90!malloc+00000079
MSVCR90!operator new+0000001F
Geometry!ESRI::Point::Clone+0000000D
EngineGraphics!GraphicTracker::GetGT+00000087
EngineGraphics!GraphicTracker::AddReplaceGeometryToDraw+00000102
EngineGraphics!GraphicTracker::AddWithOptions+0000019A
EngineGraphics!GraphicTracker::Add+00000051
mscorwks!CLRToCOMWorker+0000019A
<no module>!???+00000000 : 3CA70A

+ 1519848 ( 1519848 -      0)   9999 allocs BackTraceB522508
+    9999 (   9999 -      0) BackTraceB522508 allocations

ntdll!RtlAllocateHeap+00000274
MSVCR90!malloc+00000079
MSVCR90!operator new+0000001F
EngineGraphics!ATL::CComCreator<ATL::CComObject<GraphicTrackerObject> >::CreateInstance+00000068
EngineGraphics!ATL::CComCreator2<ATL::CComCreator<ATL::CComObject<GraphicTrackerObject> >,ATL::CComCreator<ATL::CComAggObject<GraphicTrackerObject> > >::CreateInstance+00000017
EngineGraphics!ATL::CComClassFactory::CreateInstance+00000050
ole32!CreateInprocInstanceHelper+0000001C (d:\w7rtm\com\ole32\com\objact\objact.cxx, 1469)
ole32!ICoCreateInstanceEx+000001FB (d:\w7rtm\com\ole32\com\objact\objact.cxx, 1196)
ole32!CComActivator::DoCreateInstance+000000D9 (d:\w7rtm\com\ole32\com\objact\immact.hxx, 343)
ole32!CoCreateInstanceEx+00000038 (d:\w7rtm\com\ole32\com\objact\actapi.cxx, 157)
ole32!CoCreateInstance+00000037 (d:\w7rtm\com\ole32\com\objact\actapi.cxx, 110)
AfCore!FactoryCacheDispatcher::CreateInstance+00000068
AfCore!AfCreateInstance+00000040
EngineGraphics!_com_ptr_t<_com_IIID<IGraphicTrackerObject,&_GUID_bdc460ba_4b77_4b26_9740_00099149994c> >::CreateInstance+00000031
EngineGraphics!GraphicTracker::CreateGraphicTrackerObjectWithOptions+00000049
EngineGraphics!GraphicTracker::AddWithOptions+000000DC
EngineGraphics!GraphicTracker::Add+00000051
mscorwks!CLRToCOMWorker+0000019A
<no module>!???+00000000 : 3CA70A

+  759924 ( 759924 -      0)   9999 allocs BackTraceB52259C
+    9999 (   9999 -      0) BackTraceB52259C allocations

ntdll!RtlAllocateHeap+00000274
MSVCR90!malloc+00000079
MSVCR90!operator new+0000001F
AfCore!String::CreateStringData+0000005A
AfCore!FileSystemPath::FileSystemPath+00000014
EngineGraphics!ATL::CComCreator<ATL::CComObject<GraphicTrackerObject> >::CreateInstance+00000077
EngineGraphics!ATL::CComCreator2<ATL::CComCreator<ATL::CComObject<GraphicTrackerObject> >,ATL::CComCreator<ATL::CComAggObject<GraphicTrackerObject> > >::CreateInstance+00000017
EngineGraphics!ATL::CComClassFactory::CreateInstance+00000050
ole32!CreateInprocInstanceHelper+0000001C (d:\w7rtm\com\ole32\com\objact\objact.cxx, 1469)
ole32!ICoCreateInstanceEx+000001FB (d:\w7rtm\com\ole32\com\objact\objact.cxx, 1196)
ole32!CComActivator::DoCreateInstance+000000D9 (d:\w7rtm\com\ole32\com\objact\immact.hxx, 343)
ole32!CoCreateInstanceEx+00000038 (d:\w7rtm\com\ole32\com\objact\actapi.cxx, 157)
ole32!CoCreateInstance+00000037 (d:\w7rtm\com\ole32\com\objact\actapi.cxx, 110)
AfCore!FactoryCacheDispatcher::CreateInstance+00000068
AfCore!AfCreateInstance+00000040
EngineGraphics!_com_ptr_t<_com_IIID<IGraphicTrackerObject,&_GUID_bdc460ba_4b77_4b26_9740_00099149994c> >::CreateInstance+00000031
EngineGraphics!GraphicTracker::CreateGraphicTrackerObjectWithOptions+00000049
EngineGraphics!GraphicTracker::AddWithOptions+000000DC
EngineGraphics!GraphicTracker::Add+00000051
mscorwks!CLRToCOMWorker+0000019A
<no module>!???+00000000 : 3CA70A
0 Kudos