Cesium memory leak with Ajax dynamic content loads

We have a Cesium application that cycles through a variety of pages that includes a 3D view (using Cesium). We are loading these various pages via a dynamic content load with Ajax. (similar to what they're doing with this example: http://phppot.com/jquery/dynamic-content-load-using-jquery-ajax/ )

We are having an issue that when we leave a page and close the Cesium context, the memory doesn't get freed, and (depending on the platform) after a few cycles of showing the Cesium page, we will crash because we are out of memory.

I've managed to strip this down to a fairly bare-bones example locally to eliminate all other possible factors, dynamically loading the "HellowWorld" example provided in the distribution into a dynamic Div multiple times with a button press.

The most problematic test platform is an iPad2, but the memory creep can be witnessed on any platform in Chrome via recurring heap snapshots. There is an increase of about 10MB with each subsequent load.

Is there perhaps a method I'm missing that forces WebGL/Cesium to release its held memory? I have made sure to remove all imageryLayers and primitives as well as call globe.destroy, but have had no luck and these methods have made zero impact on the memory usage.

Thanks

Are you using the Viewer widget, if so you don’t have to do any of that. All you need to do is call viewer.destroy(). If you are not using viewer and using the CesiumWidget instead, you can call destroy on that.

If you are rolling your own with using either, then there are a bunch of possible things you might have missed.

If you are calling destroy on the widgets properly, then it would be great if you could share the code so we could reproduce it.

Thanks

Hey Matt,
Thanks for getting back to me. I went through and tried to strip down our example as much as possible.

I am calling viewer.destroy() before reloading the Hello World page, but memory is still lingering.

First I had to make some minor changes to the HellowWorld test page (that's why i'm loading HelloWorld2.html). The body is this instead:
<body>
  <div id="cesiumContainer" ></div>
    <script>
    var viewer = new Cesium.Viewer('cesiumContainer');
    var container = $('#cesiumContainer');
    
    container.addClass('viewWillChangeSubscriber');
    container.bind('notify.viewWillChange', function(e, v) {
        viewer.destroy();
      });
  </script>
</body>

Now here's the code for the main html page (hopefully tis isn't too messy to follow pasted in here):

<!DOCTYPE html>
<html>
    <head>
        <title>TODO supply a title</title>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <script type="text/javascript" src="js/thirdParty/jquery/jquery-1.8.2.js"></script>
        <script src="r1_18/Build/Cesium/Cesium.js"></script>
    </head>
    <body>
        <div id="app-div">
          <div>
                    <button onclick="changeViews();">
                        Reload Map
                      </button>
          </div>
          <div id="contentContainer" style="height:500px"></div>
  </div>
        <script>
            \(setupTriggers\); &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;var wndw; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;function setupTriggers\(\) \{ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;wndw = (window);
                wndw.bind('update.viewWillChange', function(e, v) {
                    var subscribers = \(&#39;\.viewWillChangeSubscriber&#39;\); &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;subscribers\.trigger\(&#39;notify\.viewWillChange&#39;, v\); &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;\}\); &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;\} &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;var contentContainer = ('#contentContainer');
            function changeViews() {
                    wndw.trigger('update.viewWillChange', "/bluejay/dist/r1_18/Apps/HelloWorld.html");
                    contentContainer.innerHTML = "";
                    $.ajax({
                       type: "POST",
                       url: "/bluejay/dist/r1_18/Apps/HelloWorld2.html",
                       dataType: "html",
                       success: function(d) {
                           contentContainer.html(d);
                       }
                   });
            }
        </script>
    </body>
</html>

Hey Matt,
Were you able to reproduce the memory leak with the provided code example?

Any guidance or suggestions would be appreciated.

Thanks!

I’m going to take a look at this today and I’ll let you know if I’m able to reproduce it.

Thanks!

Hannah

I was able to see the memory growing in your code example, but I think it’s because viewer.destroy is never getting called.
I tried a few things, and couldn’t prove that the function was ever being executed. It’s possible I set things up differently than you did though.

Thanks,

Hannah

Hi Hannah,
I am able to confirm that viewer.destroy _is_ being called with the event mechanism I have set up in my original example.

Even with hitting viewer.destroy, the memory continues to grow.

Trubie

Hannah,
I went through and edited my example a bit more, boiling it down as much as possible so I could just post a link to the zip on Google Drive.

When you have dev tools up you should see the "debugger" line before the destroy method get hit and be able to step over viewer.destroy being called. You should then see that this has no impact on the memory and it increases by about 10MB with each successive press of the "reload" button.

Trubie

Great, thanks. How are you running the example? It didn’t work when I set up a simple node server to host the files locally.

-Hannah

Hey Hannah,
Sorry, I was testing this as part of a larger JSP solution running in Tomcat. I separated it out and slapped it into IIS and saw it wasn't working there and found the problem. Apparently the "POST" method was causing issues. To get it to work you can either change the "POST" in index.html to "GET", or you can just download a new version I put up on Google Drive:

I was able to slap this version into IIS7 and just make it an application and was good to go.

Using your example, I can recreate the problem, but I’m not sure exactly what’s going on.

I would expect the below Sandcastle example to also trigger the problem, since it’s essentially doing the same thing:

var viewer = new Cesium.Viewer(‘cesiumContainer’);

Sandcastle.addToolbarButton(‘Go’, function(){

viewer.destroy();

viewer = new Cesium.Viewer(‘cesiumContainer’);

});

But it does not. It’s possible that you are running into a problem that only happens specifically in your set up or only happens when using the combined and minified Cesium release rather than modules. I did notice that a new instance of cesiumWorkerBootstrapper gets loaded into memory every time your view reloads, I’m not sure why that’s happening but I bet it’s the key to solving this issue. I’ll write up a new Cesium issue so that I or someone else looks at this more closely.

Thanks for the detailed example.