Memory Leak with Graphic Tracker

3177
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
DubravkoAntonic
New Contributor III
It seems that there is really another leak in AO implementation. Releasing COM using different approach will not help, seem like useless effort.

I suggest that you define number of objects you would display on screen. Prefetch them and create your own pool of objects you work with and move them around the map. Lets hope memory leak is just in part of releasing Graphictracker but there could also be in releasing of handles on screen DC. Second one you would easy test in continually calling screen refresh - repaint. If you isolate leak more closely you'll easily make work around.

Hope this idea is helpful to you.

Regards, Dubravko
0 Kudos
JasonPike
Occasional Contributor
It seems that there is really another leak in AO implementation. Releasing COM using different approach will not help, seem like useless effort.

I suggest that you define number of objects you would display on screen. Prefetch them and create your own pool of objects you work with and move them around the map. Lets hope memory leak is just in part of releasing Graphictracker but there could also be in releasing of handles on screen DC. Second one you would easy test in continually calling screen refresh - repaint. If you isolate leak more closely you'll easily make work around.

Hope this idea is helpful to you.

Regards, Dubravko


Dubravko,

I agree that the problem is not with how the .NET RCWs are being released--I posted the UMDH results as evidence to support that. To be clear, the clones of the points and the new instances of the graphic tracker class were created in unmanaged code (as shown by the call stacks in the UMDH report) and therefore cannot have their reference counts decremented by .NET code.

Also, good point about a possible handle leak. While my example doesn't rule that out, it doesn't draw anything to the screen (it just creates a graphic tracker and adds and removes points), so I think it is reasonable to say that there is definitely a problem with the graphic tracker implementation. Let's hope that it isn't leaking handles as well.

Your suggestions for a workaround sound reasonable to me, so perhaps the original poster can put them to use. I just thought this problem looked interesting, so I'm looking forward to seeing if Esri will address the problem.

Thanks,
Jason
0 Kudos
JasonPike
Occasional Contributor
Any word from ESRI on this issue?

Thanks,
Jason
0 Kudos
JimFiddes
Occasional Contributor
This is a very interesting read and I'm facing some similar troubles with the GraphicTracker. I'm not receiving an out of memory error; however, I am getting a non responsive app when calling remove. Similar to the original poster I'm using 10.0 SP4 in VS2012 and have developed a mapping interface for our 911 system. The map is taking constant hits for adding and removing Calls and Incidents as well as taking hits for displaying or removing apparatus. After adding log handling around every message, it appears a common failing point is when calling the Remove(int) method. The counts on the graphic tracker are only around 500 items so no where near the 10,000 points mentioned above but needless to say I'm having trouble with the object. We choose the object as the apparatus will be (soon to come) moving around based on live GPS feeds and this object did seem to have a lot to offer in visualiztion capabilities.

When viewing the code below you will notice I have error handling all over and I'm never receiving an error. So it appears that the remove sends the machine off for a walk. The graphics card on the machine is dated and slightly below ESRI recommendations but I really hate pointing fingers at hardware whenever possible. The log files which I have viewed are not consistent on times so the error happens and doesn't happen and makes it incredibly hard to reporduce on the development side.

I will be watching this post to see if anything comes from this topic.

Below is a brief example of my clear Incident method:
public void IncidentCleared(string incID)
{
 try
 {
  GraphicItem currentItem;
  if (_cadGraphicList.TryGetValue(incID, out currentItem))
  {
   if (currentItem.GraphIdentity != -1)
   {
    try
    {
     _cadGraphicTracker.Remove(currentItem.GraphIdentity);
    }
    catch (Exception e)
    {
     if (OnErrorLog != null)
     {
      OnErrorLog(e, null);
     }
    }
   }
   _cadGraphicList.Remove(incID);
  }
 }
 catch (Exception e)
 {
  if (OnErrorLog != null)
  {
   OnErrorLog(e, null);
  }
 }
}
0 Kudos
JeremieJoalland1
Occasional Contributor II
For your information, our issue is still pending a solution from ESRI support.

so for now we only have bypassing solutions : use GraphicContainer, or as proposed ESRI support Ilayer.
in our case, as our application run 24/24 hours, we just control memory usage and automatically restart our app when necessary... waiting for a patch or any direct solution on GraphicTracker.

attached is the ILayer / CustomLayer example provided by ESRI, but with too much impact on our current code.
(C#.Net)
0 Kudos
JeremieJoalland1
Occasional Contributor II
Did somebody test Graphic Tracker with last ArcObjects version 10.2 ?
is memory leak issue still there ?
0 Kudos