Repository navigation
Conversation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
Fixes the memory growth in Chromium when
heatmaporimagetraces redraw repeatedly.Closes #8097.
Before this change, canvas-rendered heatmap and image traces assigned a new PNG data URL on every redraw. Chromium keeps the old images in memory until the page unloads. With frequent updates, that adds up quickly.
This PR uses blob URLs for those images and revokes the old URL when an image changes or leaves the plot. Purging the plot releases the remaining URLs.
Changes
The two traces now share an image URL helper in
src/lib/image_href.ts. The heatmap change also applies tohistogram2dand tocontourandhistogram2dcontourwithcontours.coloring: 'heatmap'. The internalGraphDivtype now includes_imageBlobNodesto track images that need cleanup.Canvas images in plots marked
_exportedPlotstill use data URLs.Snapshot.toSVGalso replaces a live plot's blob URLs with the saved PNG data. That keeps the exported SVG independent of the page that created it.The conversion to a blob is synchronous. I kept it that way rather than using
canvas.toBlob, so the image URL is still set beforePlotly.reactresolves. The extra cost is one base64 decode per redraw.The heatmap and image restyle tests now compare PNG data, since a redraw creates a new blob URL even if the image is unchanged. The image tests still use the original URL for images drawn directly from
source. Two new heatmap tests cover URL replacement, trace removal, purge, and SVG export throughPlotly.toImageandPlotly.Snapshot.toSVG.Testing
To reproduce, run
npm startonmain, open Chrome's task manager (Shift+Esc), and paste this into the console:On
main, the tab's memory footprint grows by about 100 MB per minute and doesn't come back down. Repeat on this branch and it levels off after a few minutes.For the image trace, use
{ type: 'image', z }with an array of[r, g, b]pixels.Here are the renderer private memory measurements from headless Chromium, with a forced GC before each sample:
heatmap, 1000×1000mainheatmap, 1000×1000image, 400×400 RGBmainimage, 400×400 RGBThe heatmap tests ran for 12 minutes and the image tests for 30 minutes. In the image runs, the memory-infra trace showed growth in
partition_alloconmain, about 0.28 MB per redraw. The JS heap stayed between 24 and 45 MB on both builds, and the screenshots were identical.Notes
There are a few cases this doesn't cover:
Plotly.Snapshot.toImageremoves its cloned plot without purging it. For canvas-rendered traces, that leaves the clone's image blob URLs allocated until the page unloads.imagetrace with a different data URL insourceon every redraw still grows memory. This PR leaves user-supplied URLs alone.Plotly.toImagecalls.