Hey three.js friends!
I’m attempting to perform an entire clean up of my three.js scene to help prevent memory leaks. As a broad overview - Our three.js application will live within a larger, non three.js app. The three.js portion can be created/close/re-created again on demand with different parameters. There’s no guarantee that the textures used during one creation will be needed/used in a future creation. Because of this, I’m looking to completely dispose of each geometry, material and texture when closed and recover the JS heap.
I’m using a cleanup method that looks like this:
cleanUp() {
let { rafRequestId, actors, modelTemplate, stadium, scene, hemiLight, dirLight, ground, ball, camera, renderer,} = context;
const disposeGeoMatsAndTextures = (c: Object3D) => {
const mesh = c as Mesh;
if (mesh.isMesh) {
mesh.geometry?.dispose();
mesh.material?.dispose();
if (mesh.material) {
for (const key of Object.keys(mesh.material)) {
const value = mesh.material[key];
if (
value &&
typeof value === "object" &&
value.dispose &&
typeof value.dispose === "function"
) {
value.dispose();
}
}
}
}
};
cancelAnimationFrame(rafRequestId);
for (const actorArray of actors.values()) {
for (const actor of actorArray) {
actor.dispose();
}
}
for (const item of scene.children) {
item.traverse(disposeGeoMatsAndTextures);
}
if (modelTemplate) {
modelTemplate.traverse(disposeGeoMatsAndTextures);
}
if (stadium) {
stadium.traverse(disposeGeoMatsAndTextures);
const existing = scene.getObjectByName(stadium.name);
if (existing) {
existing.traverse(disposeGeoMatsAndTextures);
scene.remove(existing);
}
}
if (hemiLight) {
hemiLight.traverse(disposeGeoMatsAndTextures);
}
if (dirLight) {
dirLight.traverse(disposeGeoMatsAndTextures);
}
if (ground) {
ground.traverse(disposeGeoMatsAndTextures);
}
if (ball) {
ball.group.traverse(disposeGeoMatsAndTextures);
ball.trail.trail.traverse(disposeGeoMatsAndTextures);
}
if (camera) {
camera.traverse(disposeGeoMatsAndTextures);
}
renderer.dispose();
renderer.renderLists.dispose();
console.log("clean up after", renderer.info);
}
Here you’ll see the before and after of the renderer.info
object:
You’ll see we’ve removed all memory.geometries
, but we’re left over with quite a few memory.textures
.
programs
also has only MeshDepthMaterial
left over. I’m wondering:
- Will these left over
MeshDepthMaterial
s be a problem? We’re not explicitly creating or defining them that I can see. (One thing I did notice, when I destroy our scene, then re-create it from the parent app, then destroy it again there are even moreMeshDepthMaterial
s and ourmemory.texture
count increases. This signals to me that we would have a memory leak if I leave thecleanUp
method as is.) - Is using
renderer.info
in this way an accurate test for cleaning up? Using the devtools Performance recorder isn’t showing any JSHeap recovery, but I understand that JS garbage collection is a bit mercurial and is not guaranteed to have run while I’m recording performance.
Please let me know if there’s any more info or examples I can drop in here.
Thanks!