How to dispose Class cells containing array constructors

When defining Class cells, I have trouble disposing the instantiated class in dependent cells when rerunning the original class cell. This happens when pressing enter on the Class cell multiple times, most notably when the constructor uses (typed) backing arrays.

A demonstration is available here: Rerunning Class cells that contain (Typed) Array constructors. Am I overlooking a way of properly disposing classes between reruns? It seems that sometimes these issues persist when importing said class in an external notebook and instantiating the class in multiple dependent cells.

Sharing my reply from the issue:

Be aware that classes in JS are merely an abstraction, there’s nothing special about the objects once they’ve been instantiated.

The sandbox context is shared by all of your opened notebooks, so I suspect that what you’re seeing is memory used by a different tab or notebook. Open Window > Task Manager from Chrome’s menu to see which tabs share a context.

TypedArrays are known to be slow to create since they immediately allocate memory. If you need to reuse large typed arrays frequently, consider persisting them throughout an invalidation.

Does that help answer your question?

Thanks @mootari for brainstorming potential causes. I’ve since landed on a suitable workaround, by adding a simple reuse() method to the Class cell that returns this. Dependent cells than simply have to check for whether the class has been instantiated before and reuse the class if so. This pattern is documented here.

This pattern is more performant on subsequent runs as the second time the class doesn’t need to be instantiated. If that is useful in practice remains to be seen, but at least it makes it easier to edit Class cells without eating up memory on subsequent cell reruns.

I advise against this pattern as it will likely cause unexpected behavior.

Your goal is to prevent GC cycles, so it should be sufficient to only pass the typed array like I described here:

class MyClass {
  constructor({size = 1024, init}) {
    this.data = init?.size === size
      ? (init.fill(0), init)
      : new Uint8Array(size);
  }
}
instance = new MyClass({init: this?.data})

And like I mentioned on GitHub what you’re seeing is not a memory leak because the previously allocated objects get garbage collected.

1 Like

I’ve added and updated the notebook to reflect it not being a memory leak issue. Additionally, I’ve added another (unsafe) workaround that updates the constructed class instance with properties and methods from a ‘fresh’ class. This enables editing Class cells without downstream performance being affected, by reusing the initial backing array (probably don’t use this in production).

My question then is: Why is the execution time of a downstream cell that instantiates a new Class() affected when rerunning the upstream cell containing the Class definition?

For small arrays, the performance slowdowns are not as noticeable, but becomes clearly observable when arrays grow sufficiently large.