10

I am currently having issues with freeing memory in my ThreeJS application.

I am aware there are already several questions about this problem:

I do dispose my objects using the following Typescript function I've made :

function dispose( object3D: THREE.Object3D ): void
{
    // Dispose children first
    for ( let childIndex = 0; childIndex < object3D.children.length; ++childIndex )
    {
        this.dispose( object3D.children[childIndex] );
    }

    object3D.children = [];

    if ( object3D instanceof THREE.Mesh )
    {
        // Geometry
        object3D.geometry.dispose();

        // Material(s)
        if ( object3D.material instanceof THREE.MultiMaterial )
        {
            for ( let matIndex = 0; matIndex < object3D.material.materials.length; ++matIndex )
            {
                object3D.material.materials[matIndex].dispose();
                object3D.material.materials[matIndex] = null;
            }
            object3D.material.materials = [];
        }

        if ( object3D.material.dispose )
        {
            object3D.material.dispose();
            object3D.material = null;
        }
    }

    // Remove from parent
    if ( object3D.parent )
        object3D.parent.remove( object3D );

    object3D = null;
}

However, when I do heap snapshots using Chrome Dev Tools, I still have tons and tons of :

  • Arrays
  • Vector2 (uvs in __directGeometry , ... )
  • Vector3 (vertices in geometry, normal in faces, vertexColors in faces, ...)
  • Face3 (faces in geometry)
  • Color (colors in __directGeometry, ...)
  • JSArrayBufferData (color, normal, in attributes of geometry, ...)

Because of all the data in memory, my application gets killed by Jetsam on iOS, see : Jetsam kills WebGL application on iOS

I suspect some data inside the library is not freed up when I ask it to.

Hellium
  • 7,206
  • 2
  • 19
  • 49
  • 2
    It sounds more like Jetsam is being overly aggressive. JavaScript is garbage-collected, so taking a heap snapshot immediately after releasing objects may not be 100% reflective of what the memory state will be after GC. Do the objects ever go away (like after 1 minute)? If not, then there may be other references to them in the code which have not been released, because GC can only collect an object if nothing references it. – TheJim01 Jun 01 '17 at 13:34
  • Unfortunately, it seems they are still in the memory, even after 1 minute (which is quite a long time, I thought the GC would do its job more often). _`there may be other references to them in the code which have not been released`_, yes, this is the only reason why those objects would not be cleared from the memory. @TheJim01 : Do you know if there is a "simple" way to "tag" an object in code, so as to retrieve it easily in the Chrome Dev Tools so that I can see where it's referenced? – Hellium Jun 01 '17 at 13:47
  • Sadly, I don't know of any easy way to find the references. If you find out, let me know! :) Aside from that, I would reduce the problem to its simplest form: write a very simple JavaScript-only app (no TypeScript), and see if it performs the same way. If it does, then I'm not sure what else you can do. – TheJim01 Jun 01 '17 at 14:06
  • I believe you are looking for this: https://stackoverflow.com/a/33199591/1980846 – gaitat Jun 06 '17 at 01:43
  • @gaitat : The provided dispose function is the same as mine except it disposes the maps too, but that's not a problem for me. – Hellium Jun 06 '17 at 05:31
  • i would recommend console.log'ing your scene after disposal and make sure it's not hanging around anywhere.Also if you have any persistent objects that use references to any other objects that get stored locally, then there will be persistent references. ie, if some player object collects positions of obstacles or meshes from them and keeps them locally, then if the positions are passed by reference, they won't get released. – OtterFamily Jun 06 '17 at 10:35
  • A tool which has helped me find memory leaks in our threejs application is Heapviz: https://heapviz.com/ Just drop the chrome snapshots in and have them nicely visualized. – teh.fonsi Feb 12 '20 at 06:43

1 Answers1

0

Try to dispose just everything. I'm using this snippet for a while. It disposes materials, textures, 3d objects. It iterates over arrays and plain objects.

let dispose = function(o) {
    try {
        if (o && typeof o === 'object') {
            if (Array.isArray(o)) {
                o.forEach(dispose);
            } else
            if (o instanceof THREE.Object3D) {
                dispose(o.geometry);
                dispose(o.material);
                if (o.parent) {
                    o.parent.remove(o);
                }
                dispose(o.children);
            } else
            if (o instanceof THREE.Geometry) {
                o.dispose();
            } else
            if (o instanceof THREE.Material) {
                o.dispose();
                dispose(o.materials);
                dispose(o.map);
                dispose(o.lightMap);
                dispose(o.bumpMap);
                dispose(o.normalMap);
                dispose(o.specularMap);
                dispose(o.envMap);
            } else
            if (typeof o.dispose === 'function') {
                o.dispose();
            } else {
                Object.values(o).forEach(dispose);
            }
        }
    } catch (error) {
        console.log(error);
    }
};
SeregPie
  • 1,223
  • 1
  • 18
  • 20