maptiler / maptiler-sdk-js

Maps SDK tailored for MapTiler Cloud powered by MapLibre GL JS
https://docs.maptiler.com/sdk-js/
BSD 3-Clause "New" or "Revised" License
71 stars 14 forks source link

Memory leak #76

Closed zarskilukasz closed 3 months ago

zarskilukasz commented 5 months ago

Hello, I have a suspicion of a memory leak. I am working on a real estate service, and for the maps, I use MapTiler SDK JS along with custom markers to denote property locations.

While navigating to the offer page and then returning to the map, I've noticed that the memory is not being released. The more I navigate this way, memory usage grows exponentially by about 10 - 15mb for each return to the map, reaching over 300mb after just a few minutes of using the map.

For the purpose of testing and possibly identifying where the issue lies, I replaced MapTiler SDK JS with MapLibre GL JS. To my surprise, by conducting the same scenario as above, the memory leak issue does not occur - it is released almost back to the initial values, in my case to about 70mb, which is a substantial difference.

For the test, the map style has been disabled. Unfortunately, I cannot change the mapping tool because I need the custom map coloring provided by MapTiler.

I am attaching screenshots.

MapTiler SDK JS - 2.0.0 MapLibre GL JS - 4.1.2
Zrzut ekranu 2024-04-3 o 13 51 27 Zrzut ekranu 2024-04-3 o 14 00 41
Test environment
MacBook Air M2 / 16GB
MacOS: 14.3.1 (23D60)
Chrome: 123.0.6312.59
Lib Version
@maptiler/sdk 2.0.0
maplibre-gl 4.1.2

I have also conducted tests on other devices and browsers, and the results were the same. Mobile devices fared the worst in this case, where after some time it was noticeable that the browser was struggling with such high memory

jonathanlurie commented 5 months ago

Hello @zarskilukasz , thanks for raising this issue, i will looking it! Are you adding anything else specific than the markers?

zarskilukasz commented 5 months ago

Hi @jonathanlurie, thank you for the quick response. I add only the markers (built with HTML using JS), which open a Popup when clicked. I don't know if this will help... but I also conducted a different test where, instead of generating a Marker, I added a Source and Layer (based on the example from https://docs.maptiler.com/sdk-js/examples/popup-on-click/), and here as well the outcome was.

jonathanlurie commented 5 months ago

Thanks for the details @zarskilukasz , this should help narrow down the issue.

jonathanlurie commented 3 months ago

Hello @zarskilukasz , apologies for the very long delays but I could finally take some time to perform some benchmarking. Here is how I went:

To test this issue, I have created a 3-page website:

Scenario 1: with MapTiler SDK v2.0.3

On Chrome Version 124.0.6367.203 (Official Build) (arm64) - Macbook Air M2 (16GB RAM)

Scenario 2: with MapLibre GL JS v4.3.2

On Chrome Version 124.0.6367.203 (Official Build) (arm64) - Macbook Air M2 (16GB RAM)

Conclusion

No significant difference in memory management between MapLibre GL JS and MapTiler SDK. Feel free to let me know if my testing scenario is missing a relevant bit, making it too different from your real-world case.

Here are the sources of the three pages:

with-none.html:

<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>>>>>>>>> Memory Leak SDK vs ML</title>
<style>
  body {margin: 0; padding: 0;}
  #map {position: absolute; top: 0; bottom: 0; width: 100%;}
  #links {position: absolute; z-index: 2; background-color: white; padding: 10px;}
</style>
</head>
<body>
<div id="links">
  <div><a href="/with-sdk">Go to SDK page</a></div>
  <div><a href="/with-ml">Go to ML page</a></div>
</div>
</body>
</html>

with-ml.html:

<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>>>>>>>>> Memory Leak ML</title>
<script src="https://cdn.jsdelivr.net/npm/maplibre-gl@4.3.2/dist/maplibre-gl.min.js"></script>
<link href="https://cdn.jsdelivr.net/npm/maplibre-gl@4.3.2/dist/maplibre-gl.min.css" rel="stylesheet">
<style>
  body {margin: 0; padding: 0;}
  #map {position: absolute; top: 0; bottom: 0; width: 100%;}
  #links {position: absolute; z-index: 2; background-color: white; padding: 10px;}
</style>
</head>
<body>
<div id="map"></div>
<div id="links">
  <div><a href="/with-sdk">Go to SDK page</a></div>
  <div><a href="/with-none">Go to Home</a></div>
</div>
<script>
    var map = new maplibregl.Map({
        container: 'map',
        style: "https://api.maptiler.com/maps/streets-v2/style.json?key=MAPTILER_API_KEY",
        center: [10, 47],
        zoom: 5,
    });

    map.on('load', function () {

      const bounds = map.getBounds()
      const west = bounds.getWest()
      const east = bounds.getEast()
      const north = bounds.getNorth()
      const south = bounds.getSouth()

      // Adding markers to the map
      const nbMarkers = 1000;
      for (let i = 0; i < nbMarkers; i += 1) {
        new maplibregl.Marker()
          .setLngLat([
            Math.random() * (east - west) + west,
            Math.random() * (north - south) + south
          ])
          .addTo(map);
      }
    });
</script>
</body>
</html>

with-sdk.html:

<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>>>>>>>>> Memory Leak SDK</title>
<script src="https://cdn.maptiler.com/maptiler-sdk-js/v2.0.3/maptiler-sdk.umd.js"></script>
<link href="https://cdn.maptiler.com/maptiler-sdk-js/v2.0.3/maptiler-sdk.css" rel="stylesheet" />
<style>
  body {margin: 0; padding: 0;}
  #map {position: absolute; top: 0; bottom: 0; width: 100%;}
  #links {position: absolute; z-index: 2; background-color: white; padding: 10px;}
</style>
</head>
<body>
<div id="map"></div>
<div id="links">
  <div><a href="/with-ml">Go to ML page</a></div>
  <div><a href="/with-none">Go to Home</a></div>
</div>
<script>
    maptilersdk.config.apiKey = 'MAPTILER_API_KEY';
    var map = new maptilersdk.Map({
        container: 'map',
        style: maptilersdk.MapStyle.STREETS,
        center: [10, 47],
        zoom: 5,
    });

    map.on('load', function () {

      const bounds = map.getBounds()
      const west = bounds.getWest()
      const east = bounds.getEast()
      const north = bounds.getNorth()
      const south = bounds.getSouth()

      // Adding markers to the map
      const nbMarkers = 1000;
      for (let i = 0; i < nbMarkers; i += 1) {
        new maptilersdk.Marker()
          .setLngLat([
            Math.random() * (east - west) + west,
            Math.random() * (north - south) + south
          ])
          .addTo(map);
      }
    });
</script>
</body>
</html>