Leaflet / Leaflet

🍃 JavaScript library for mobile-friendly interactive maps 🇺🇦
https://leafletjs.com
BSD 2-Clause "Simplified" License
40.28k stars 5.76k forks source link

Memory not freed with layer groups and `clearLayers()` #8538

Open nsilvestri opened 1 year ago

nsilvestri commented 1 year ago

Checklist

Steps to reproduce

  1. Create a layer group
  2. Add one or more layers to it
  3. call layerGroup.clearLayers()
  4. Repeat steps 2 and 3 until you run out of memory

Example code:

let layerGroup = L.layerGroup();
layerGroup.addTo(map);

function getRandomInt(min, max) {
  min = Math.ceil(min);
  max = Math.floor(max);
  return Math.floor(Math.random() * (max - min) + min); // The maximum is exclusive and the minimum is inclusive
}

for (let i = 0; i <= 9999999; i++) {
  layerGroup.clearLayers();
  let latlngs = [];
  for (let j = 0; j < 3; j++) {
    let latlng = [];
    latlng.push(getRandomInt(-90, 90));
    latlng.push(getRandomInt(-180, 180));
    latlngs.push(latlng);
  }
  var polygon = L.polygon(latlngs, {color: 'red'}).addTo(layerGroup);
}

Expected behavior

After I clearLayers(), I am no longer holding any references to the layer, nor is it being displayed on the map. The garbage collector should clean up the reference and any Leaflet objects that were created from it.

Current behavior

The memory consumption grows until the tab crashes, even though I'm only displaying one layer before clearing it. The provided example balloons to >15GB on my machine before crashing.

Minimal example reproducing the issue

https://plnkr.co/plunk/SSoPyUYk5kQjsFv3

Environment

adrianaris commented 1 year ago

I may be confused but I believe your test is wrong.

You do remove incremental layers but in the end you still add to the final layer a polygon made out of 10 million polyline objects.

Just a simple JS object is about 250 bytes(https://stackoverflow.com/questions/58631581/javascript-compare-object-size-in-memory), that is about 2,4GB worth of memory for 10 mil objects. Perhaps a polyline is 5 to 10 times larger than a simple object plus the color property plus the final polygon and you get your 15GB!?

nsilvestri commented 1 year ago

latlngs and each coordinate pair are initialized in the loop, so their values should be reset with every cycle. The only layer in the group and on the map after the final iteration is a single triangle. You can verify this by reducing the number of iterations in the loop to a smaller amount: the final map will display only a single layer.

adrianaris commented 1 year ago

You are right, I got confused.

adrianaris commented 1 year ago

For me the ballooning-up happens on Firefox and on Chrome it doesn't: image

While going throw the debugger I can see that the actual clearing happens: image

I get the feeling that the issue is with Firefox: https://bugzilla.mozilla.org/show_bug.cgi?id=1633626 https://bugzilla.mozilla.org/show_bug.cgi?id=1343234 https://bugzilla.mozilla.org/show_bug.cgi?id=1251596

john-a-m commented 3 weeks ago

I believe I am also having this issue, I have a piece of code that essentially does

layerGroup.addTo(map);
layerGroup.removeFrom(map);

however it appears that somehow the map is remembering the layerGroup, and as I add and remove various layerGroups the heap becomes quite large. I'm trying to find where the data is being kept using Chrome's memory profile tools, no luck. Has anyone discovered a workaround?