Is .dispose() even needed?


why is dispose() method needed? I see now I have memory leaks if I ramp up the number of objects, but if I keep no other references to the object, why won’t garbage collector get rid of it?

For BufferGeometry, the dispose method triggers an event, which calls onGeometryDispose. This method ensures the attribute references are removed from the object (freed for garbage collection). It also does bookkeeping, tracking the number of geometries handled by three.js (line 57).

For Material objects, the dispose method triggers an event, which follows a chain of methods. It first calls onMaterialDispose, which calls deallocateMaterial, which finally calls releaseMaterialProgramReference (all in three.js/src/renderers/WebGLRenderer.js).

In deallocateMaterial it removes the material reference from the WebGLProperties.

In releaseMaterialProgramReference, it un-sets the material’s program (line 561), then releases the program from the program cache (line 565).

TL;DR: Even though your Mesh may get garbage collected, that does not mean that the low-level supporting objects were also released, because some of those low-level objects are referenced in other parts of three.js which aren’t obvious on the surface. Calling dispose ensures these low-level objects are properly freed from internal caches, allowing them to be fully garbage-collected.

(All code in reference to three.js r94.)


Ah ok, I came from C/C++ to Javascript and I was relieved no more deallocation for me, didn’t know functions could create variables that won’t get collected. Great, thanks!

I don’t know if that’s conceptually correct. We may already be on the same page, but I’ll offer an example just in case there’s some misunderstanding.

class Position {
  constructor( x, y, z ) {
    this.x = x
    this.y = y
    this.z = z

class PointCache {
  constructor() {
    this.points = []
  add( point ) {
    this.points.push( point )
  remove( point ) {
    let idx = this.points.indexOf( point )
    if (idx > -1) {
      this.points.splice( idx, 1 )

class Example {
  constructor( point, cache ) {
    this.point = point
    this.cache = cache
    cache.add( point )
  dispose() {
    this.point = undefined

// Instantiate the objects
let pt = new Point( 1, 2, 3 )
let c = new PointCache()
let ex = new Example( pt, c )

// Setting these to undefined doesn't release the objects to garbage collection
// because other objects still hold references to them
pt = undefined
c = undefined

// Calling dispose on the Example to prepare it and its sub-objects for garbage collection
ex = undefined
// The Example is now ready to be collected, but...

…the above code is leaky. PointCache will retain a reference to pt, even though the other references to pt were set to undefined. Therefore, the Point represented by pt will never get garbage collected.

But if we change Example.dispose like this…

  dispose() {
    this.cache.remove( this.point )
    this.point = undefined

…then the PointCache will discard its reference to the Point. When dispose finishes, all references to the Point have been nullified, so the Point is freed for garbage collection.

1 Like

It also removes stuff from the GPU.

Maybe I didn’t look at the code closely enough, but I didn’t see where it was doing that. Could you link to where it tells the GPU to discard buffers?

Edit: Nevermind, I found it. The attributes.remove call ends up calling gl.deleteBuffer, and materials end up calling gl.deleteProgram.

I actually thought that you only need it for that. Even when you call foo.dispose() you still have foo. You have to do foo = undefined to release it.

The webgl stuff though on the other hand has absolutely no idea what you’re doing with your garbage collection in javascript, probably doesn’t know it exists. So if you release something that holds pointers to your webgl objects… you cant release those objects, you dont know how to access them.

At least until you close the tab, and the entire context gets discarded.

Yeah, managing JavaScript object references can be a pain, but I can definitely see why the primary purpose of dispose is to deallocate GPU resources.

1 Like