Memory Leak creating symbology for Legend Control

3188
8
06-24-2014 10:28 AM
Labels (1)
Cristian_Galindo
Occasional Contributor III
DEAR PEOPLE FROM THE FUTURE: Here's what we've figured out so far???

I developed an application that creates a GraphicLayer and draw in it 827 graphics (polylines) using the ArcGIS runtime 10.2.2, each object has it own symbology defined using the following code to create the renderer:

private UniqueValueRenderer GetUniqueValueRenderer(GraphicsLayer layer, string attributeField)
        {
            var uniqueValueRenderer = new UniqueValueRenderer();
            var defaultSymbol = new ESRI.ArcGIS.Client.Symbols.SimpleLineSymbol { Width = 2.5 };
            uniqueValueRenderer.Field = attributeField;

            var colorRange = new ColorRange { From = this.GetRandomColor(), To = this.GetRandomColor() };

            var rampInterpolator = new RampInterpolator { ColorRange = colorRange };

            foreach (var graphic in layer.Graphics)
            {
                var valueInfo = new UniqueValueInfo
                            {
                                Label = graphic.Attributes[attributeField].ToString(),
                                Value = graphic.Attributes[attributeField].ToString(),
                                Symbol =
                                    rampInterpolator.GetInterpolatedSymbol(
                                            defaultSymbol, 
                                            graphic, 
                                            0, 
                                            layer.Graphics.Count, 
                                            layer.Graphics.IndexOf(graphic))
                            };
                uniqueValueRenderer.Infos.Add(valueInfo);
            }

            return uniqueValueRenderer;
        }


In a workflow, I added the layer, remove it from the list of layers and then added again. In the last step of this workflow, the application launch the following exception:

Exception from HRESULT: 0x88980003 in the PresentationCore.dll with the following callstack:

PresentationCore.dll!System.Windows.Media.Imaging.RenderTargetBitmap.FinalizeCreation() + 0xd8 bytes 
  PresentationCore.dll!System.Windows.Media.Imaging.RenderTargetBitmap.RenderTargetBitmap(int pixelWidth, int pixelHeight, double dpiX, double dpiY, System.Windows.Media.PixelFormat pixelFormat) + 0x169 bytes 
  ESRI.ArcGIS.Client.dll!ESRI.ArcGIS.Client.LegendSymbol.CreateImageSnapshot() + 0x2c8 bytes 
  ESRI.ArcGIS.Client.dll!ESRI.ArcGIS.Client.LegendSymbol.ToImageSource.AnonymousMethod__1() + 0x2b bytes 
  WindowsBase.dll!System.Windows.Threading.ExceptionWrapper.InternalRealCall(System.Delegate callback, object args, int numArgs) + 0x128 bytes 
  WindowsBase.dll!MS.Internal.Threading.ExceptionFilterHelper.TryCatchWhen(object source, System.Delegate method, object args, int numArgs, System.Delegate catchHandler) + 0x47 bytes 
  WindowsBase.dll!System.Windows.Threading.DispatcherOperation.InvokeImpl() + 0x281 bytes 
  mscorlib.dll!System.Threading.ExecutionContext.RunInternal(System.Threading.ExecutionContext executionContext, System.Threading.ContextCallback callback, object state, bool preserveSyncCtx) + 0x285 bytes 
  mscorlib.dll!System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext executionContext, System.Threading.ContextCallback callback, object state, bool preserveSyncCtx) + 0x9 bytes 
  mscorlib.dll!System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext executionContext, System.Threading.ContextCallback callback, object state) + 0x57 bytes 
  WindowsBase.dll!System.Windows.Threading.DispatcherOperation.Invoke() + 0x71 bytes 
  WindowsBase.dll!System.Windows.Threading.Dispatcher.ProcessQueue() + 0x2a1 bytes 
  WindowsBase.dll!System.Windows.Threading.Dispatcher.WndProcHook(System.IntPtr hwnd, int msg, System.IntPtr wParam, System.IntPtr lParam, ref bool handled) + 0xb3 bytes 
  WindowsBase.dll!MS.Win32.HwndWrapper.WndProc(System.IntPtr hwnd, int msg, System.IntPtr wParam, System.IntPtr lParam, ref bool handled) + 0x14a bytes 
  WindowsBase.dll!MS.Win32.HwndSubclass.DispatcherCallbackOperation(object o) + 0x80 bytes 
  WindowsBase.dll!System.Windows.Threading.ExceptionWrapper.InternalRealCall(System.Delegate callback, object args, int numArgs) + 0x5a bytes 
  WindowsBase.dll!MS.Internal.Threading.ExceptionFilterHelper.TryCatchWhen(object source, System.Delegate method, object args, int numArgs, System.Delegate catchHandler) + 0x47 bytes 
  WindowsBase.dll!System.Windows.Threading.Dispatcher.LegacyInvokeImpl(System.Windows.Threading.DispatcherPriority priority, System.TimeSpan timeout, System.Delegate method, object args, int numArgs) + 0x2bc bytes 
  WindowsBase.dll!MS.Win32.HwndSubclass.SubclassWndProc(System.IntPtr hwnd, int msg, System.IntPtr wParam, System.IntPtr lParam) + 0x140 bytes 
  [Native to Managed Transition] 
  [Managed to Native Transition] 
  WindowsBase.dll!System.Windows.Threading.Dispatcher.PushFrameImpl(System.Windows.Threading.DispatcherFrame frame) + 0x112 bytes 
  PresentationFramework.dll!System.Windows.Application.RunInternal(System.Windows.Window window) + 0x17a bytes 
  PresentationFramework.dll!System.Windows.Application.Run() + 0x67 bytes 
  Rosen.Clients.Infrastructure.IntegrityManagement.exe!Rosen.Clients.Infrastructure.IntegrityManagement.App.Main() + 0x77 bytes C#


If i set a SimpleRenderer for the layer, it does not show any issue.
Reading the call stack and watching the behavior of the application, the symbols on the map are drawn but the application crash whe it tries to create the symbology for the legend control.

so far i have been trying to release memory when the layer is removed:


public void RemoveLayer()
        {
            var layerItemViewModels = this.SelectedLayers;
            if (layerItemViewModels != null)
            {
                var layerItemViewModel = layerItemViewModels.FirstOrDefault();
                if (layerItemViewModel != null)
                {
                    var selectedLayer = layerItemViewModel.Layer.DisplayName;
                    var layerToRemove = layerItemViewModel.Layer;
                    if (this.VisibleLayers.Contains(layerToRemove) && !(layerToRemove is ArcGISLocalTiledLayer || layerToRemove is ArcGISTiledMapServiceLayer))
                    {
                        var group = layerToRemove as GroupLayer;
                        if (group != null)
                        {
                            var layerIdList = group.GetLayerIdListFromGroup();
                            foreach (var layerId in layerIdList)
                            {
                                this.VisibleLayers.RemoveLayer(layerId);
                            }
                        }
                        else
                        {
                            this.VisibleLayers.RemoveLayer(layerToRemove.ID);
                        }

                        this.VisibleLayers.Remove(layerToRemove); // aqui se remueve el layer
                        if (layerToRemove is GraphicsLayer)
                        {
                            this.CleanLayerInstances(layerToRemove as GraphicsLayer);
                            this.CleanLayerItemViewModel(layerItemViewModel);
                        }
                        GC.Collect();
                        GC.WaitForPendingFinalizers();
                        GC.Collect();

                    }
                    else
                    {
                        foreach (var layer in this.VisibleLayers)
                        {
                            if (layer is GroupLayer)
                            {
                                this.RemoveLayerFromGroupLayer(layer as GroupLayer, selectedLayer);
                            }
                        }
                    }
                }
            }
        }

        private void CleanLayerItemViewModel(LayerItemViewModel layerItemViewModel)
        {
            foreach (var legendItem in layerItemViewModel.LegendItems.ToArray())
            {
                layerItemViewModel.LegendItems.Remove(legendItem);
            }
        }

        private void CleanLayerInstances(GraphicsLayer layer)
        {
            foreach (var graphic in layer.Graphics.ToList())
            {
                    if (graphic.Geometry is Polyline)
                    {
                        var line = graphic.Geometry as Polyline;
                        foreach (var path in line.Paths.ToList())
                        {
                            line.Paths.Remove(path);
                            path.Clear();
                        }
                        
                        line.Paths = null;
                    }

                graphic.MapTip = null;
                graphic.Geometry = null;
                layer.Graphics.Remove(graphic);
            }
            
            layer.Graphics = null;
        }


So far I still get the same exception
0 Kudos
8 Replies
Cristian_Galindo
Occasional Contributor III
I forgot to add the exception:

System.Runtime.InteropServices.COMException occurred
  HResult=-2003304445
  Message=Exception from HRESULT: 0x88980003
  Source=PresentationCore
  ErrorCode=-2003304445
  StackTrace:
       at System.Windows.Media.Imaging.RenderTargetBitmap.FinalizeCreation()
  InnerException:
0 Kudos
MichaelBranscomb
Esri Frequent Contributor
Hi,

You are creating a UniqueValueRenderer with 827 different values? ...and trying to display 827 symbol swatches in the legend?

Perhaps a ClassBreaksRenderer might be a better option?

Cheers

Mike
0 Kudos
Cristian_Galindo
Occasional Contributor III
Yes  I do.....Why??  The client wants it... 😞

I tried to suggest the use of ClassBreaksRenderer but the requirement states that each object is different so each one must have its own representation in the legend control.
0 Kudos
MichaelBranscomb
Esri Frequent Contributor
Hi

By the way, I wasn't denying there may be an issue for us to investigate in the Legend, I was just surprised at what you are trying to do since typical map/app/UX design suggests the legend should be considered a backup to the map itself and each layer/renderer in the legend should have no more than 5-7 classes because the human eye/brain finds it difficult to work with more.

I'll discuss with one of the developers.

Cheers
0 Kudos
Cristian_Galindo
Occasional Contributor III
Thanks Mike!!

Either way I will try to discuss the point that you address with the analyst.
0 Kudos
MichaelBranscomb
Esri Frequent Contributor
Hi,

Unfortunately I have not been able to reproduce the exception. I tested with 1000 graphics, and therefore 1000 UniqueValueInfos. Please can you provide a small, self contained reproducer which demonstrates this issue?

Cheers

Mike
0 Kudos
MichaelBranscomb
Esri Frequent Contributor
...Some other things you could try though:
- Force a garbage collection (GC.Collect, etc)
- When you are cleaning up, include the renderer (and symbols)

Cheers

Mike
0 Kudos
Cristian_Galindo
Occasional Contributor III
I am already forcing the garbage collector (twice) when I am cleaning up....although I did not clean the renderers...but the map does not show any issue when creating renderer, because the map shows all graphics....either way I'm going to do it..it will free more memory.
0 Kudos