Three.js: Deallocating heap objects when removing mesh from scene

Created on 7 Aug 2014  Â·  32Comments  Â·  Source: mrdoob/three.js

As mentioned from StackOverflow: http://stackoverflow.com/questions/25126352/deallocating-buffergeometry

The good news is, it's not a bug. The bad news is it's a change in the system that might catch a lot of people off-guard.

It looks like the old (< r68) way of handling object deallocation is _webglObjects is refreshed with each call to render. When you remove Mesh from the scene and nullify it, the _webglObjects refreshes--sans the Mesh--on the next render. After that, GC can safely remove the Mesh form the heap.

In r69dev, there is no magical refresh of _webglObjects. Removing and nullifying a Mesh leaves a reference to the Mesh in _webglObjects. GC then isn't be able to remove the Mesh (or even the geometry/material) from the heap, resulting in my "leak."

The thing I missed between r68 and r69dev is that when you remove a mesh in r69dev, you need to explicitly call dispose on it before nullifying it. Calling dispose triggers removeObject for the Mesh, which updates _webglObjects array. No more references lets GC happily collect the Mesh and associated data.

I'm mentioning it as a concern because bringing r68 code into r69dev was mostly seamless, and this issue wouldn't have been on my radar if I weren't dealing with large amounts of geometry. Also, it's slightly odd not _needing_ to call dispose on Object3D objects that are part of the scene, though I did see the method now exists.

Enhancement

Most helpful comment

If it helps, this is the dispose I have for scene BTW -maybe overkill, but seemed to work for my memory tests, you might have better.

passing the scene to the function :

function doDispose (obj)
    {
        if (obj !== null)
        {
            for (var i = 0; i < obj.children.length; i++)
            {
                doDispose(obj.children[i]);
            }
            if (obj.geometry)
            {
                obj.geometry.dispose();
                obj.geometry = undefined;
            }
            if (obj.material)
            {
                if (obj.material.map)
                {
                    obj.material.map.dispose();
                    obj.material.map = undefined;
                }
                obj.material.dispose();
                obj.material = undefined;
            }
        }
        obj = undefined;
    };

Also I have this for renderer :

 renderer.dispose();
   renderer.forceContextLoss(); 
   renderer.context=undefined;
   renderer.domElement=undefined;

All 32 comments

Yes. It's a matter of choosing between:

  1. Automatically disposing objects when removed from the scene
  2. Not being able to render the same scene with 2 different WebGLRenderers.

We've not being able to do 2 for a while. The side effect of being able to do 2 is that we can't now do 1.

Also, it's slightly odd not needing to call dispose on Object3D objects that are part of the scene, though I did see the method now exists.

That may be needed, actually.

Ahhh, I was too busy looking at the heap to look at the actual flow of operations!

I see now that there is special handling for immediate objects, and it also de-flags the object's active state. Despite the heap getting cleaned up, I've added the Object3D.dispose() to my cleanup routine for good practice.

I guess this is more of a documentation enhancement than anything else. Feel free to close this topic, unless you need a reminder for dev. :)

I find this topic incredibly confusing for some reason...

  1. What is now the proper way to remove an object or mesh from the scene -- and, optionally, dispose of it?
  2. How would this procedure change if the mesh shares its geometry, and/or material, and/or textures, with other meshes that are not disposed of?

What is now the proper way to remove an object or mesh from the scene -- and, optionally, dispose of it?

Before it used to be like this:

scene.remove( mesh );

geometry.dispose();
material.dispose();
texture.dispose();

Now is like this:

scene.remove( mesh );

mesh.dispose(); // new
geometry.dispose();
material.dispose();
texture.dispose();

How would this procedure change if the mesh shares its geometry, and/or material, and/or textures, with other meshes that are not disposed of?

If you call geometry.dispose() and another Mesh still uses it, the buffers will get recreated on the next render (or at least that how it should be).

I've added the Object3D.dispose() to my cleanup routine for good practice.

This is something I didn't thought about. I'll keep that in mind when completing this change.

@mrdoob Thank you for the explanation.

If you call geometry.dispose() and another Mesh still uses it, the buffers will get recreated on the next render (or at least that is how it should be).

Self-healing. That's awesome!

Just a thought: If it is indeed self-healing in this way, could geometry/material/texture disposal be made part of mesh disposal? More library legwork, cleaner client code.

If you call geometry.dispose() and another Mesh still uses it, the buffers will get recreated on the next render (or at least that is how it should be).

Self-healing. That's awesome!

Actually. The custom WebGLRenderer I'm doing for the game supports enabling/disabling antialiasing with a click. Enabling/disabling antialiasing means creating a new context and creating all the buffers again. It works beautifully and without losing a frame! Coming soon to the "official" WebGLRenderer ;)

Just a thought: If it is indeed self-healing in this way, could geometry/material/texture disposal be made part of mesh disposal? More library legwork, cleaner client code.

Hmm... geometries or materials may be being used by other objects...

Continued in #5269.

Did mesh.dispose(); get removed from Three.js? I'm using version 71 and I don't see the function my meshes.

Is there any new ways to dispose meshes? I cannot call "dispose" on meshes either.

I'm pretty sure meshes don't dispose the only geometry textures and
materials, the components of meshes.
On Jun 3, 2015 7:52 AM, "SinaEghbal" [email protected] wrote:

Is there any new ways to dispose meshes? I cannot call "dispose" on meshes
either.

—
Reply to this email directly or view it on GitHub
https://github.com/mrdoob/three.js/issues/5175#issuecomment-108467114.

I've already disposed of them but that didn't deallocate the memory.

Objects in three.js stay in memory if there is a reference to them. Try and
see if it's still in the scene or a separate list your maintaining
On Jun 3, 2015 8:11 AM, "SinaEghbal" [email protected] wrote:

I've already disposed of them but that didn't deallocate the memory.

—
Reply to this email directly or view it on GitHub
https://github.com/mrdoob/three.js/issues/5175#issuecomment-108477484.

Removing and assigning null didnt help.

As I said before I'm not sure if Memory Leaks are your problem. You can look in chrome's debugger and do a snapshot of your heap, search for THREE.Mesh and investigate exactly what's keeping it in memory.

I find dispose mesh, it has been changed in version 71? How should I do now?

Mesh no longer has dispose().

So how should I take the scene? Because I saw some ways and all of them have failed.
I'm opening the 3D in a Modal and entering objs it, then close the modal and open again, and objs are still there. I wanted what I did the first time not back up when I opened up. I remove them from the scene every time I close with screen.remove(obj [n]), and still see them inserted the first time.

What I use anywhere do you possess in version 71 to remove the memory of objects?

How do we dispose mesh in the latest version ?

You can use this example as reference:

http://threejs.org/examples/#webgl_test_memory

Thanks !

You can use this example as reference:

http://threejs.org/examples/#webgl_test_memory

That's all well and good if you create everything explicitly. But has anyone created an ObjectLoader that can be used and cleaned up after? In the rambling, undocumented parse() method, it creates materials and geometry willy-nilly, returning just a container with the mesh in it. Without hanging onto the stuff it created, is there a way to rip through the mesh itself and get all the materials and geometry in a reliable way so they can be disposed?

Here's a start:

scene.traverse( function ( object ) {

    if ( object.geometry ) geometry.dispose();
    if ( object.material ) material.dispose();

} );

Thanks for the quick turnaround on that. Inspecting the hierarchy of the scene in Chrome, I'd about decided that would be the a easiest approach. Couple of questions though:

1) Shouldn't we remove the object from the scene in that same loop?

2) Wouldn't it make sense for THREE.Scene to have a dispose() method that does this?

3) And what about textures? In your webgl_test_memory example, you hang onto them and dispose of them. I don't know how to get to them with this approach.

And what about textures? In your webgl_test_memory example, you hang onto them and dispose of them. I don't know how to get to them with this approach.

Looks like material.map. So maybe...

scene.traverse( function ( object ) {

    if ( object.geometry ) object.geometry.dispose();
    if ( object.material ) {
        if (object.material.map) object.material.map.dispose();
        object.material.dispose();
    }
    scene.remove(object);
} );

Shouldn't we remove the object from the scene in that same loop?

Why? Dispose is for deallocating stuff from GPU memory.

Wouldn't it make sense for THREE.Scene to have a dispose() method that does this?

Maybe, yeah... would you like doing a PR?

Well in the world of web demos you rarely need to tear down, I get that. But in my case I'm building a visualizer where you can enter and exit visualization mode on different projects and the app really needs to manage memory well. It seems cumbersome to make all apps that need this to have to implement the same logic in addition to freeing their own references.

So, yeah, I'll submit a PR shortly since you're open to it.

Also, props to you sir, for this crazy awesome tool!

Cheers,
-=Cliff>

You bet. I'll get to work on it.

Sent from my iPhone

On Jul 1, 2017, at 7:00 PM, Mr.doob notifications@github.com wrote:

Shouldn't we remove the object from the scene in that same loop?

Why? Dispose is for deallocating stuff from GPU memory.

Wouldn't it make sense for THREE.Scene to have a dispose() method that does this?

Maybe, yeah... would you like doing a PR?

—
You are receiving this because you commented.
Reply to this email directly, view it on GitHub, or mute the thread.

If it helps, this is the dispose I have for scene BTW -maybe overkill, but seemed to work for my memory tests, you might have better.

passing the scene to the function :

function doDispose (obj)
    {
        if (obj !== null)
        {
            for (var i = 0; i < obj.children.length; i++)
            {
                doDispose(obj.children[i]);
            }
            if (obj.geometry)
            {
                obj.geometry.dispose();
                obj.geometry = undefined;
            }
            if (obj.material)
            {
                if (obj.material.map)
                {
                    obj.material.map.dispose();
                    obj.material.map = undefined;
                }
                obj.material.dispose();
                obj.material = undefined;
            }
        }
        obj = undefined;
    };

Also I have this for renderer :

 renderer.dispose();
   renderer.forceContextLoss(); 
   renderer.context=undefined;
   renderer.domElement=undefined;
Was this page helpful?
0 / 5 - 0 ratings