I am encountering numerous challenges in eliminating memory leaks while utilizing three.js within a reactive application. After investigating, I discovered that even disposing of a scene correctly (without rendering) poses difficulties. Allow me to demonstrate:
https://plnkr.co/edit/Z0sXOnXYc2XOV4t9tETv
In the above example, initially, 3 THREE objects are instantiated (no rendering, just object instantiation):
- scene
- camera
- renderer
By utilizing Chrome DevTools, let's capture a memory snapshot immediately after loading the page:
https://i.sstatic.net/SbQ7j.png
Now, let's click on the "ADD 1000 MESHES" button, which essentially creates 1000 meshes (BoxGeometry + MeshBasicMaterial) and adds them to the scene object. After taking another memory snapshot, we can compare it with the previous one:
https://i.sstatic.net/osroc.png
As observed, our memory usage increased from 25.2 Mb to 36.2 Mb, with an additional +1000 Mesh objects stored in memory.
Next, by clicking the "DISPOSE" button, we execute 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!")
}
This function traverses through the scene, disposing of every geometry and material. The references to scene, camera, and renderer are set to null, and listeners are removed to prevent memory leaks. Upon clicking the DISPOSE button and capturing a new memory snapshot, my expectation was that the garbage collector would clear all data related to the 1000 Meshes (Mesh, Matrix4, Vector3, BoxGeometry, etc…). However, the reality proved to be quite different:
https://i.sstatic.net/flRvt.png
Although it appears that 1000 Mesh objects were deleted, the memory usage remains nearly identical to the previous snapshot (34.6 vs 36.2 Mb). While there are drops in Vector3, Matrix4, Quaternion, and Euler objects, most objects persist in memory and avoid being collected by the garbage collector. In fact, comparing snapshots 3 and 1 confirms this:
https://i.sstatic.net/gvD4f.png
If possible, could someone elaborate on what is happening and provide guidance on correctly disposing of elements in three.js?
Three.js: 102 Google Chrome: 72.0.3626.121 (64-bit)