In my use case it's not hard to catch the memory growth. There are only a few entry points, and I can check the allocated views afterward. The problem is managing the references to the views that have spread throughout the application state.
I can write a Proxy to a view that can swap out the underlying view, but as I mentioned, it's too slow. Turns out it's more performant to subclass the view. The underlying view can be swapped out by setting __proto__. Not a great solution, and still 10x slower to access than the view w/o subclassing, but faster than a Proxy.
The main places in the code holding references are memoized functions, so another approach might be to add special handling to the memoizers, to swap out the views in cache when they are invalidated.