3
votes

I am having many problems to get rid of memory leaks using three.js in a reactive application. Investigating the problem, I found that I am not even able to correctly dispose a scene (without rendering). Let me show you:

https://plnkr.co/edit/Z0sXOnXYc2XOV4t9tETv

In the above example, initially 3 THREE objects are instantiated (as you can see there is no rendering going on, just instantiating the objects):

  • scene
  • camera
  • renderer

Using the chrome devtools let’s take a memory snapshot just after loading the page:

enter image description here

Now let’s click on the “ADD 1000 MESHES” button, which as you can guess simply create 1000 meshes (BoxGeometry + MeshBasicMaterial) and add them to the scene object. Let’s take another memory snapshot and see the comparison (delta) with the previous snapshot:

enter image description here

As you can see we passed from 25.2 Mb to 36.2 Mb and there are +1000 Mesh objects added in memory.

Now clicking the “DISPOSE” button we’ll trigger the following dispose function:

 const dispose = (e) => {           

    // dispose geometries and materials in scene
    sceneTraverse(scene, o => {

        if (o.geometry) {
            o.geometry.dispose()
            console.log("dispose geometry ", o.geometry)                        
        }

        if (o.material) {
            if (o.material.length) {
                for (let i = 0; i < o.material.length; ++i) {
                    o.material[i].dispose()
                    console.log("dispose material ", o.material[i])                                
                }
            }
            else {
                o.material.dispose()
                console.log("dispose material ", o.material)                            
            }
        }
    })          

    scene = null
    camera = null
    renderer && renderer.renderLists.dispose()
    renderer = null

    addBtn.removeEventListener("click", addMeshes)
    disposeBtn.removeEventListener("click", dispose)

    console.log("Dispose!")
}

In this function we traverse the scene and dispose every geometry and material. Then we set to null the references to scene, camera and renderer and finally we remove the listeners to avoid memory leaks. Let’s click on DISPOSE button and take another memory snapshot. I expected that the garbage collector will completely remove from memory all the data related to the 1000 Meshes (Mesh, Matrix4, Vector3, BoxGeometry, etc…) but if we take another memory snapshot we’ll find something very different:

enter image description here

It seems that 1000 Mesh objects have been deleted, but the memory usage is almost the same as in the previous snapshot (34.6 vs 36.2 Mb). There are some drops in the Vector3, Matrix4, Quaternion and Euler objects, but most of the objects keep to be persisted in memory and are not collected from the garbage collector. Indeed, if we compare the snapshot 3 to the snapshot 1 we find exactly that:

enter image description here

Please could someone explain what’s going on and how correctly dispose things in three.js?

Three.js: 102 Google Chrome: 72.0.3626.121 (64-bit)

1
Thanks, tried but no differencerevy
I have the exact same problem. Calling dispose on the materials, and geometries does not clear memory fully. Removing mesh from scene doesn’t clear fully. And removing console logs doesn’t clear fully. Lol.Joe C

1 Answers

5
votes

Actually the problem were the console.log statements which prevent to garbage-collect the objects printed on the chrome console. Removing the console.log statements solved the problem.