How to Prevent Memory Leaks in Three.js
In Three.js, failing to properly clean up unused assets can quickly lead to severe memory leaks, browser slowdowns, and application crashes. Because JavaScript’s automatic garbage collection cannot release memory allocated on the GPU, WebGL resources must be freed manually. This article provides a direct, step-by-step guide on how to properly dispose of geometries, materials, and textures to keep your 3D applications running efficiently.
Why Manual Disposal is Necessary
When you create objects like geometries, materials, and textures,
Three.js allocates memory on both the CPU (JavaScript heap) and the GPU
(WebGL buffers). Removing a mesh from your scene using
scene.remove(mesh) only stops it from rendering; it does
not free the GPU memory. To release these resources, you must explicitly
call the .dispose() method on each component.
Disposing of Geometries
Geometries store vertex attributes, faces, and index buffers on the
GPU. To free this memory, call .dispose() directly on the
geometry instance.
geometry.dispose();Once disposed of, the geometry cannot be rendered again. If you need to use it later, you will have to create a new instance.
Disposing of Textures
Textures represent substantial image data sent to the GPU. To release
this memory, call .dispose() on the texture object.
texture.dispose();If you loaded the texture using a TextureLoader, you
must still keep a reference to the loaded texture to call its dispose
method when it is no longer needed.
Disposing of Materials
Materials define how objects look and link shaders and textures together. Disposing of a material frees the WebGL program (shaders) associated with it.
material.dispose();Crucial Note: Disposing of a material does not automatically dispose of its map textures. You must loop through the material’s properties and dispose of any attached textures individually.
if (material.map) material.map.dispose();
if (material.lightMap) material.lightMap.dispose();
if (material.bumpMap) material.bumpMap.dispose();
// Repeat for other texture slots if used
material.dispose();Cleaning Up a Mesh Completely
To fully remove a mesh from memory, you must remove it from the scene parent, dispose of its geometry, dispose of its material (including textures), and nullify any references to allow JavaScript garbage collection to clean up CPU memory.
Here is a clean helper function to recursively dispose of a 3D object and all of its children:
function disposeHierarchy(object) {
object.traverse((child) => {
if (child.isMesh) {
// Dispose Geometry
if (child.geometry) {
child.geometry.dispose();
}
// Dispose Material(s)
if (child.material) {
if (Array.isArray(child.material)) {
child.material.forEach((material) => disposeMaterial(material));
} else {
disposeMaterial(child.material);
}
}
}
});
}
function disposeMaterial(material) {
// Dispose textures
for (const key in material) {
if (material[key] && typeof material[key].dispose === 'function') {
material[key].dispose();
}
}
// Dispose the material itself
material.dispose();
}
// Usage:
scene.remove(myMesh);
disposeHierarchy(myMesh);
myMesh = null;Disposing of Render Targets
If you are using post-processing, custom shaders, or shadow maps, you
likely use WebGLRenderTarget. These also consume
significant GPU memory and must be cleaned up:
renderTarget.dispose();By integrating these disposal practices into your object destruction lifecycles, you ensure that GPU and CPU resources are fully reclaimed, preventing memory leaks in long-running Three.js applications.