0

I have a MapControl where I add MapIcons and Polylines. After a few minutes of work app starts leaking unmanaged memory and goes until 8Gb.

Please, help me to find a bug. Thank you!

I have used dotMemory profiler. The screenshot below showing leaking memory.

enter image description here

C#

Every second I redraw a MapControl (mainMap).

 _timerUpdateDevices = ThreadPoolTimer.CreatePeriodicTimer(async (t) =>
                {
                   // ... some managed code to prepare geodata ...

                    await mainMap.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, async () =>
                    {
                        if (Interlocked.Read(ref canInsertNewCoordsForSelectedDeviceTracker) == 1)
                        {
                            RenderTracks(deviceID, tracker);
                        }
                        else
                        {
                            var trackLayer = cmap.CommonLayers.First(x => x.CMLayerType == CMLayerTypeEnum.Track);
                            var trackMapLayer = mainMap.Layers.FirstOrDefault(x => x.ZIndex == trackLayer.ZIndex);
                            if (trackMapLayer != null)
                            {
                                if (((MapElementsLayer)trackMapLayer).MapElements.Count > 0) ((MapElementsLayer)trackMapLayer).MapElements.Clear();
                            }
                        }
                        await RenderMap();
                    });
                }, TimeSpan.FromSeconds(1));


   private void RenderTracks(int deviceID, LGPSTracker tracker)
        {
            try
            {
                if (userSelectedDevice != null)
                {
                    #region Track layer 
                    
                    var trackMapLayer = mainMap.Layers.FirstOrDefault(x => x.ZIndex == cmap.CommonLayers.First(x => x.CMLayerType == CMLayerTypeEnum.Track).ZIndex);

                    if (selectedDeviceTrackerPositions.Any() && selectedDeviceTrackerPositions.Count >= 2)
                    {                        
                        if (((MapElementsLayer)trackMapLayer).MapElements.Count > 0) // Update
                        {
                            ((MapElementsLayer)trackMapLayer).MapElements.Clear(); // Clean

                            LTrackerPosition[][] chunks = null; 

                            lock (selectedDeviceTrackerPositionsLock)
                            {
                                chunks = selectedDeviceTrackerPositions.Select((s, i) => new { Value = s, Index = i }).GroupBy(x => x.Index / 2).Select(grp => grp.Select(x => x.Value).ToArray()).ToArray();
                            }

                            LTrackerPosition lastInChunck = null;
                            for (int i = 0; i < chunks.Length; i++)
                            {
                                if (chunks[i].Length != 2) continue;

                                var firstPoint = chunks[i][0];
                                var secondPoint = chunks[i][1];

                                var mapPolyline = new MapPolyline();
                                var geoPositions = new List<BasicGeoposition>();
                                if (lastInChunck != null)
                                {
                                    geoPositions.Add(new BasicGeoposition() { Latitude = lastInChunck.Lat, Longitude = lastInChunck.Lng });
                                }
                                geoPositions.Add(new BasicGeoposition() { Latitude = firstPoint.Lat, Longitude = firstPoint.Lng });
                                geoPositions.Add(new BasicGeoposition() { Latitude = secondPoint.Lat, Longitude = secondPoint.Lng });
                                mapPolyline.Path = new Geopath(geoPositions);
                                mapPolyline.StrokeColor = Colors.Blue;
                                mapPolyline.StrokeThickness = trackMapPolylineStrokeThickness;
                                mapPolyline.Tag = new TrackerView() { Position = firstPoint, Device = userSelectedDevice, Tracker = tracker };
                                ((MapElementsLayer)trackMapLayer).MapElements.Add(mapPolyline);

                                lastInChunck = chunks[i][1];
                            }                            
                        }
                        else // Add
                        {
                            LTrackerPosition[][] chunks = null;

                            lock (selectedDeviceTrackerPositionsLock)
                            {
                                chunks = selectedDeviceTrackerPositions.Select((s, i) => new { Value = s, Index = i }).GroupBy(x => x.Index / 2).Select(grp => grp.Select(x => x.Value).ToArray()).ToArray();
                            }

                            LTrackerPosition lastInChunck = null;
                            for (int i = 0; i < chunks.Length; i++)
                            {
                                if (chunks[i].Length != 2) continue;

                                var firstPoint = chunks[i][0];
                                var secondPoint = chunks[i][1];

                                var mapPolyline = new MapPolyline();
                                var geoPositions = new List<BasicGeoposition>();
                                if (lastInChunck != null)
                                {
                                    geoPositions.Add(new BasicGeoposition() { Latitude = lastInChunck.Lat, Longitude = lastInChunck.Lng });
                                }
                                geoPositions.Add(new BasicGeoposition() { Latitude = firstPoint.Lat, Longitude = firstPoint.Lng });
                                geoPositions.Add(new BasicGeoposition() { Latitude = secondPoint.Lat, Longitude = secondPoint.Lng });
                                mapPolyline.Path = new Geopath(geoPositions);
                                mapPolyline.StrokeColor = Colors.Blue;
                                mapPolyline.StrokeThickness = trackMapPolylineStrokeThickness;
                                mapPolyline.Tag = new TrackerView() { Position = firstPoint, Device = userSelectedDevice, Tracker = tracker };
                                ((MapElementsLayer)trackMapLayer).MapElements.Add(mapPolyline);

                                lastInChunck = chunks[i][1];
                            }
                        }
                    }

                    #endregion                    
                }
            }
            catch (Exception ex)
            {
                Debug.WriteLine(ex.Message);
            }
        }


  private async Task RenderMap()
        {
            var devices = ((App)Application.Current).Devices;
            var trackers = ((App)Application.Current).Trackers;
            var deviceTrackers = ((App)Application.Current).DeviceGPSTrackers;

            foreach (var clayer in cmap.CommonLayers)
            {
                if (clayer.CMLayerType == CMLayerTypeEnum.Transport)
                {
                    #region Tranport layer

                    var layer = mainMap.Layers.FirstOrDefault(x => x.ZIndex == clayer.ZIndex);
                    if (layer != null)
                    {
                        if (((MapElementsLayer)layer).MapElements.Count == 0)
      {
                            List<TrackerView> trackerPoints = new List<TrackerView>();
                            var isCoordsInterpolating = DefaultSettings.GetIsCoordsInterpolating();
                            if (isCoordsInterpolating)
                            {
                                if (clayer.TrackerPoints.Count == 0) return;
                                trackerPoints = clayer.TrackerPoints.Select(d => d.Value).ToList<TrackerView>();
                            }
                            else
                            {
                                if (clayer.TrackerPoints.Count == 0) return;
                                trackerPoints = clayer.TrackerPoints.Select(d => d.Value).ToList<TrackerView>();
                            }

                            foreach (var trackerPoint in trackerPoints)
                            {
                                var bg = new BasicGeoposition { Latitude = trackerPoint.Position.Lat, Longitude = trackerPoint.Position.Lng };

                                var mi = new MapIcon
                                {
                                    Location = new Geopoint(bg),
                                    NormalizedAnchorPoint = new Point(0.5, 1.0),
                                    ZIndex = 0,
                                    Tag = trackerPoint,
                                    IsEnabled = true,
                                    Visible = true,
                                    CollisionBehaviorDesired = MapElementCollisionBehavior.RemainVisible,

                                };

                                mi.Image = await GetDeviceStateIconStreamNormal(trackerPoint.Device.DeviceTypeID, 2, trackerPoint.Device.Name);

                                ((MapElementsLayer)layer).MapElements.Add(mi); 
                            }
                        }
                        else 
                        {
                            #region Add cars

                            List<TrackerView> trackerPoints = new List<TrackerView>();
                            var isCoordsInterpolating = DefaultSettings.GetIsCoordsInterpolating();
                            if (isCoordsInterpolating)
                            {
                                if (clayer.TrackerPoints.Count == 0) return;
                                trackerPoints = clayer.TrackerPoints.Select(d => d.Value).ToList<TrackerView>();
                            }
                            else
                            {
                                if (clayer.TrackerPoints.Count == 0) return;
                                trackerPoints = clayer.TrackerPoints.Select(d => d.Value).ToList<TrackerView>();
                            }

                            var toAdd = new List<MapIcon>();

                            foreach (var trackerPoint in trackerPoints)
                            {
                                var isLayerElement = ((MapElementsLayer)layer).MapElements.FirstOrDefault(x => ((TrackerView)x.Tag).Device.ID == trackerPoint.Device.ID);
                                if (isLayerElement == null) // Добавить
                                {
                                    var bg = new BasicGeoposition { Latitude = trackerPoint.Position.Lat, Longitude = trackerPoint.Position.Lng };

                                    var mi = new MapIcon
                                    {
                                        Location = new Geopoint(bg),
                                        NormalizedAnchorPoint = new Point(0.5, 1.0),
                                        ZIndex = 0,
                                        Tag = trackerPoint,
                                        IsEnabled = true,
                                        Visible = true,
                                        CollisionBehaviorDesired = MapElementCollisionBehavior.RemainVisible,
                                    };

                                    if (userSelectedDevice != null) // Сделать прозрачным весьтранспорт кроме выделенного
                                    {
                                        if (userSelectedDevice.ID == trackerPoint.Device.ID)
                                        {
                                            mi.Image = await GetDeviceStateIconStreamSelected(trackerPoint.Device.DeviceTypeID, 2, trackerPoint.Device.Name);
                                        }
                                        else
                                        {
                                            mi.Image = await GetDeviceStateIconStreamFireless(trackerPoint.Device.DeviceTypeID, 2, trackerPoint.Device.Name, 0.4f);
                                        }
                                    }
                                    else // Update
                                    {
                                        mi.Image = await GetDeviceStateIconStreamNormal(trackerPoint.Device.DeviceTypeID, 2, trackerPoint.Device.Name);
                                    }

                                    ((MapElementsLayer)layer).MapElements.Add(mi);
                                    
                                }
                                else // Update
                                {
                                    var mi = (MapIcon)isLayerElement;
                                    var posMapElement = mi.Location.Position;
                                    if (posMapElement.Latitude != trackerPoint.Position.Lat || posMapElement.Longitude != trackerPoint.Position.Lng)
                                    {
                                        var bg = new BasicGeoposition { Latitude = trackerPoint.Position.Lat, Longitude = trackerPoint.Position.Lng };
                                        ((MapIcon)isLayerElement).Location = new Geopoint(bg);

                                        if (userSelectedDevice != null) 
                                        {
                                            if (userSelectedDevice.ID == trackerPoint.Device.ID)
                                            {
                                                mi.Image = await GetDeviceStateIconStreamSelected(trackerPoint.Device.DeviceTypeID, 2, trackerPoint.Device.Name);
                                            }
                                            else
                                            {
                                                mi.Image = await GetDeviceStateIconStreamFireless(trackerPoint.Device.DeviceTypeID, 2, trackerPoint.Device.Name, 0.5f);
                                            }
                                        }
                                        else 
                                        {
                                            mi.Image = await GetDeviceStateIconStreamNormal(trackerPoint.Device.DeviceTypeID, 2, trackerPoint.Device.Name);
                                        }                                        
                                    }
                                }
                            }

                            #endregion

                            #region Delete it from map

                            foreach (var device in devices.Where(x => x.IsEnabled == 0))
                            {
                                var isLayerElementExisting = ((MapElementsLayer)layer).MapElements.FirstOrDefault(x => ((TrackerView)x.Tag).Device.ID == device.ID);
                                if (isLayerElementExisting != null)
                                {
                                    ((MapElementsLayer)layer).MapElements.Remove(isLayerElementExisting);
                                }
                            }

                            #endregion
                        }

                        break;
                    }
                    #endregion
                }
            }
        } // private void RenderMap()

// Create  map icon on fly
private async Task<IRandomAccessStreamReference> GetDeviceStateIconStreamNormal(int deviceTypeID, int deviceStateID, string text)
        {
            try
            {
                var deviceTypeIcon = deviceTypeIcons.FirstOrDefault(x => x.DeviceTypeId == deviceTypeID && x.DeviceStateId == deviceStateID);
                if (deviceTypeIcon != null)
                {
                    var stream = new MemoryStream(deviceTypeIcon.IconFile);
                    var randomAccessStream = new InMemoryRandomAccessStream();
                    var outputStream = randomAccessStream.GetOutputStreamAt(0);
                    await RandomAccessStream.CopyAndCloseAsync(stream.AsInputStream(), outputStream);
                    var imagedecoder = await BitmapDecoder.CreateAsync(randomAccessStream);
                    var device = CanvasDevice.GetSharedDevice();
                    var renderTarget = new CanvasRenderTarget(device, imagedecoder.PixelWidth, imagedecoder.PixelHeight, 96);

                    using (var ds = renderTarget.CreateDrawingSession())
                    {
                        ds.Clear(Colors.Transparent);
                        Color color = Colors.Black;

                        var image = await CanvasBitmap.LoadAsync(device, randomAccessStream, 96);
                        var destinationRectangle = new Rect() { Width = imagedecoder.PixelWidth, Height = imagedecoder.PixelHeight, X = 1, Y = 1 };
                        var sourceRectangle = new Rect() { Width = imagedecoder.PixelWidth, Height = imagedecoder.PixelHeight, X = 1, Y = 1 };
                        ds.DrawImage(image, destinationRectangle, sourceRectangle);

                        ds.DrawText(text, new System.Numerics.Vector2(2, 1), color, new CanvasTextFormat()
                        {
                            FontSize = 12,
                            FontWeight = Windows.UI.Text.FontWeights.Bold
                        });
                    }

                    await renderTarget.SaveAsync(randomAccessStream, CanvasBitmapFileFormat.Png);
                    await randomAccessStream.FlushAsync();
                    return RandomAccessStreamReference.CreateFromStream(randomAccessStream);
                }
                else
                {
                    return null; // TODO use default image
                }
            }
            catch (Exception ex)
            {
                Debug.WriteLine(ex);
            }
            return null;
        }
rbrundritt
  • 16,570
  • 2
  • 21
  • 46
NoWar
  • 36,338
  • 80
  • 323
  • 498
  • Have you done any memory profiling? Are you leaking any event handlers or other managed objects that may own unmanaged memory? – JonasH Aug 03 '21 at 07:47
  • @JonasH Yes I did, pls see updated question. – NoWar Aug 03 '21 at 07:56
  • 1
    You have not answered my second question. It is for possible that event-handlers are added but never removed, and that these hold on to native memory. To investigate these types of problems you need to check the object graph and see if there are any leaked objects or event-handlers. – JonasH Aug 03 '21 at 08:19
  • 1
    I don't see any leaking memory on this part of the dotMemory timeline chart. Could you please show whole dotMemory views. 1. Timeline chart 2. Snapshot inspections (take a snapshot for this and open "Inspections" view) Why are you thinking that there is a memory leak? – Ed Pavlov Aug 03 '21 at 09:15

1 Answers1

1

Thanks to @JonasH and @Ed Pavlov.

This code does not have any bugs provocation for memory leaks.

I found a couple of leaks with some lambda expresiion += (s,e) => for Tile's method of MapControl. So I've fixed it.

Anyway just making Zoom in/out of UWP MapControl during in a minutes increase memory a lots.

NoWar
  • 36,338
  • 80
  • 323
  • 498