Chrome tab memory grows from ~150MB to ~500MB+ over 30 minutes. Observed on /live/?kiosk page.
Globe.gl's ring digest never releases ring data objects. Every ring object passed to globe.ringsData() is retained in the digest's internal Map permanently, even after being removed from subsequent calls.
Verified with FinalizationRegistry:
ring=52/0 (52 created, 0 ever garbage collected)ring=291/263 (normal finalization)Bug report filed at ../globe.gl/BUG.md.
Replaced dynamic ring creation/destruction with a fixed object pool. The pool is passed to globe.ringsData() once at init. On updates, we mutate pool objects' lat/lng properties in place. Since object identity never changes, the digest's add/remove path is never triggered, avoiding the leak entirely.
globe.pointOfView(target, duration) accumulated Tween objects in an internal group that never pruned finished tweens. Each call with duration > 0 added a tween permanently. In kiosk mode this was called on every shield event.
Replaced with smoothPointOfView() — custom requestAnimationFrame animation that calls globe.pointOfView(coords, 0) (duration=0 = no tween created).
Systematic isolation via console overrides, monitoring performance.memory and renderer.info.memory every 30s, and FinalizationRegistry tracking.
| Test | js:total trend | Conclusion |
|---|---|---|
| Baseline | +5–25 MB/min, accelerating | Leak confirmed |
eventSource.close() | Flat | Leak tied to SSE event handling |
eventSource.close() fresh | Flat at 132 | Render loop, audio, clock don't leak |
onmessage = noop | ~0.3 MB/min | Not EventSource buffer |
onmessage = parse-only | Flat | Not JSON.parse |
| Test | js:total trend | Conclusion |
|---|---|---|
updateEventHistory = noop | Growing | Not DOM updates |
smoothPointOfView = noop | Growing | Not camera animation |
processShieldData = noop | Flat | Leak is in processShieldData |
processEcomscanData alone | Flat | ecomscan not leaking |
| Test | js:total trend | Conclusion |
|---|---|---|
globe.ringsData = noop | Growing | Not just ringsData call |
| All globe methods + handlers nooped | Growing | Not globe.gl API calls |
| All nooped, fresh reload | Flat at 128 | Contaminated by prior state |
| Object type | Created/Finalized | Leaking? |
|---|---|---|
| Arc | 52/44 | No |
| Glow | 52/44 | No |
| Ring | 52/0 | Yes — never GC'd |
| Test | Ring GC | js:total | Conclusion |
|---|---|---|---|
globe.ringsData = noop | 68/51 (normal) | Flat | Ring digest retains objects |
| Ring pool fix (mutate in place) | 291/263 (normal) | Stable ~110MB | Fixed |