CesiumGS / CesiumGS/cesium

Height callbacks leak memory when the globe is hidden

Open
#13,784 1 comment 0 reactions 0 assignees View on GitHub
needs triage type - bug
Dominant language
JavaScript
Stars
15.7k
Forks
3.9k
Avg merge
4d 6h
Merged PRs (30d)
34

Description

### What happened?

When globe.show is false, QuadtreePrimitive._removeHeightCallbacks grows by one entry for every height callback that is removed. Each entry has a Cartographic. Not a problem when globe.show is true.

[Sandcastle showing the problem](https://sandcastle.cesium.com/#c=jVbbbuM2EP2VqboPNuLQcpGirR2nLbzB7kO2CbJp+yIgocmRRIQiBXLkywb594Kk7LVT9yIYhjiXMzOcOWMLazzBSuEaHczB4BoW6FXXsD+ibFBkIp4X1hBXBl2RjeClMABL7vGGb9FNoeTa4+hIeKfE87GqQiusPJS9DmeFKYyIOXiBBmHe58LicbZTVtougzJKWTxF1/EYHmoEYU2pqs5xUtYA1cqDr+3aQ9eCMlPgQEqjRwJhV+g8UI1QOdsZOQJv0zGGUD6C+rUiUaMEW5asMFHHAiTMU/a76H/WnIBDY1fKVCA0b1qUoAgbkBY94ArdFkrHG5yCw0p5QgccalRVTSC41ksunqG0DtY1OgRFEVh5MHYN3Ehw2NgVxiStwSNTWHPP4NqQIoUepE3FB5Ot7YBqZ7uqjoAfY8R7LNGhEcgWN79+unt8uH38cH/7+2/vZyBtKEERSOVQkN7CM2Kb7go3vGk1gm+41mzXldZ6FW98vhuaBXdkK8fbWglWOtu8x8oh+sH5D9+zfAQXOctD0zVSX9adw5WynY8XmtrbOrxHI9ExLuX1Cg3dhGsz6AZlZ0SMOBimIdylwLQ1laJOIpzts/nEqWZk77lU3PhBzvI8n4TwAKqEQW8lsVQG5eA4n2EfAN7kOUj+r+HrWLMfz66VnDDd9yBh7NIcpeNRGa+9sE/nP/oUjUMOPXnGY3hMeSTHRT9RPkxQ69SKE45g2VHorErd5M7xLVAY3crZtd9TsHMlF4FnaeAfe0HfsDjFocz8gLYtN6hhDtKKrkFDTDjkhNcaw2lQZFKtiizkGi2Zp61GJrx/wA3BPFRTZLvrmfKlt7ojnJFtp5O83cw0lpTevpwrI3EzneR5PgslJgJPXbXkg4vvRumTs5+GsyKDs4QsrLZu+i3KsizLWculVKaKeDC5aDezpXUS3bnjUnV+GiSlNTQNOmissb7lAotsv4gccmk7+reCW4ep4N62L7nhrlKBKkWWHwAuO6LIoH/ESxYJMr0zwg2FhYyGIuDnsJn2Syyi95aHsR9sG61/bDeHJtYIrcRz2Gxv2XW09775eooUOJnLgcfPUGQflcTDxGB6OtvX/XzwtkUjF7XSgZHxAocnlSl80O1vbmnl9sgmeg0PV4v19L93y9lZmvhZ4npq5lG5ibhPB0UfPe9evmpei8I8pakEeOqp9Lfn3UvSHFs/cinfsjtZ9wRlJyyYRlNR/Qbp9Ko4RDppsQN7SosnG2WXcbKuAu4vqmmtI+icHjA2JmxazQn9eNmJZ6RA9tCDy/HO5VKqFSg5P/HfIvyEej8vsrLT+rP6gkV2dTmWanXkpi0PPL5dodN8G0zqydVNEjLGLsf15IQXWauX3B0g/gU)

Claude's analysis:

1. Scene.updateHeight registers a callback on the globe's quadtree whenever a globe exists — it checks defined(this.globe), not globe.show ([Scene.js#L4336](https://github.com/CesiumGS/cesium/blob/488b114e16f5879f5d51456640aae67850a715c0/packages/engine/Source/Scene/Scene.js#L4336)). So a hidden globe still accumulates callbacks.

2. QuadtreePrimitive.updateHeight's removeFunc pushes onto _removeHeightCallbacks unconditionally ([QuadtreePrimitive.js#L292](https://github.com/CesiumGS/cesium/blob/488b114e16f5879f5d51456640aae67850a715c0/packages/engine/Source/Scene/QuadtreePrimitive.js#L292)), including when the callback is still sitting in _addHeightCallbacks and the tiles have never been told about it. In that case there is nothing for them to remove.

3. Both arrays are drained only in selectTilesForRendering ([QuadtreePrimitive.js#L549](https://github.com/CesiumGS/cesium/blob/488b114e16f5879f5d51456640aae67850a715c0/packages/engine/Source/Scene/QuadtreePrimitive.js#L549)), which is reached from QuadtreePrimitive.render, and Globe.render returns at its first line when show is false ([Globe.js#L1086](https://github.com/CesiumGS/cesium/blob/488b114e16f5879f5d51456640aae67850a715c0/packages/engine/Source/Scene/Globe.js#L1086)).

Impact:

In our application — about 100 objects are clamped over Google Photorealistic 3D Tiles, so the globe is hidden — a heap snapshot showed 674,000 retained objects on this array after a few minutes. Because the live set has to be walked on every collection, the cost shows up as growing garbage collection pauses rather than as an obvious out-of-memory.

Claude's suggested fix in packages/engine/Source/Scene/QuadtreePrimitive.js:

object.removeFunc = function () {
const addedCallbacks = primitive._addHeightCallbacks;
const length = addedCallbacks.length;
+ let stillWaiting = false;
for (let i = 0; i < length; ++i) {
if (addedCallbacks[i] === object) {
addedCallbacks.splice(i, 1);
+ stillWaiting = true;
break;
}
}
- primitive._removeHeightCallbacks.push(object);
+ if (!stillWaiting) {
+ primitive._removeHeightCallbacks.push(object);
+ }
if (object.callback) {
object.callback = undefined;
}
};

### Reproduction steps

1. Run the Sandcastle, observe length of _removeHeightCallbaks increasing each frame without bounds
2. Click "show the globe" - _removeHeightCallbacks remains 0

### Sandcastle example

https://sandcastle.cesium.com/#c=jVbbbuM2EP2VqboPNuLQcpGirR2nLbzB7kO2CbJp+yIgocmRRIQiBXLkywb594Kk7LVT9yIYhjiXMzOcOWMLazzBSuEaHczB4BoW6FXXsD+ibFBkIp4X1hBXBl2RjeClMABL7vGGb9FNoeTa4+hIeKfE87GqQiusPJS9DmeFKYyIOXiBBmHe58LicbZTVtougzJKWTxF1/EYHmoEYU2pqs5xUtYA1cqDr+3aQ9eCMlPgQEqjRwJhV+g8UI1QOdsZOQJv0zGGUD6C+rUiUaMEW5asMFHHAiTMU/a76H/WnIBDY1fKVCA0b1qUoAgbkBY94ArdFkrHG5yCw0p5QgccalRVTSC41ksunqG0DtY1OgRFEVh5MHYN3Ehw2NgVxiStwSNTWHPP4NqQIoUepE3FB5Ot7YBqZ7uqjoAfY8R7LNGhEcgWN79+unt8uH38cH/7+2/vZyBtKEERSOVQkN7CM2Kb7go3vGk1gm+41mzXldZ6FW98vhuaBXdkK8fbWglWOtu8x8oh+sH5D9+zfAQXOctD0zVSX9adw5WynY8XmtrbOrxHI9ExLuX1Cg3dhGsz6AZlZ0SMOBimIdylwLQ1laJOIpzts/nEqWZk77lU3PhBzvI8n4TwAKqEQW8lsVQG5eA4n2EfAN7kOUj+r+HrWLMfz66VnDDd9yBh7NIcpeNRGa+9sE/nP/oUjUMOPXnGY3hMeSTHRT9RPkxQ69SKE45g2VHorErd5M7xLVAY3crZtd9TsHMlF4FnaeAfe0HfsDjFocz8gLYtN6hhDtKKrkFDTDjkhNcaw2lQZFKtiizkGi2Zp61GJrx/wA3BPFRTZLvrmfKlt7ojnJFtp5O83cw0lpTevpwrI3EzneR5PgslJgJPXbXkg4vvRumTs5+GsyKDs4QsrLZu+i3KsizLWculVKaKeDC5aDezpXUS3bnjUnV+GiSlNTQNOmissb7lAotsv4gccmk7+reCW4ep4N62L7nhrlKBKkWWHwAuO6LIoH/ESxYJMr0zwg2FhYyGIuDnsJn2Syyi95aHsR9sG61/bDeHJtYIrcRz2Gxv2XW09775eooUOJnLgcfPUGQflcTDxGB6OtvX/XzwtkUjF7XSgZHxAocnlSl80O1vbmnl9sgmeg0PV4v19L93y9lZmvhZ4npq5lG5ibhPB0UfPe9evmpei8I8pakEeOqp9Lfn3UvSHFs/cinfsjtZ9wRlJyyYRlNR/Qbp9Ko4RDppsQN7SosnG2WXcbKuAu4vqmmtI+icHjA2JmxazQn9eNmJZ6RA9tCDy/HO5VKqFSg5P/HfIvyEej8vsrLT+rP6gkV2dTmWanXkpi0PPL5dodN8G0zqydVNEjLGLsf15IQXWauX3B0g/gU

### Environment

Browser: Chrome 148 and in QtWebEngine 6.11 (Chromium 140) and Edge
CesiumJS Version: 1.145
Operating System: Windows 11

### AI acknowledgment

- [x] I used AI to generate this issue report.
- [x] (If the above is checked) I have reviewed the AI-generated content before submitting.

Contributor guide

Open the contributing guide

Research direction

Start in packages/engine/Source/Scene/QuadtreePrimitive.js, especially updateHeight and the _addHeightCallbacks and _removeHeightCallbacks handling described in the issue. Run the linked Sandcastle reproduction with the globe hidden and shown. Done means removed callbacks no longer accumulate while the globe is hidden, while normal callback removal still works.

Written by the indexing model from the issue text.

Assessment

Tech stack
javascript
Domain
performance
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Active
Clarity
Clearly specified
Newbie friendliness
76/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.