In our web application we’ve noticed a significant number of detached DOM subtrees not getting garbage collected when running in Safari Desktop and on iOS. We’ve managed to narrow the issue down to img elements with loading=“lazy” set on them, that have not been loaded yet. The retained (leaking) subtrees in question correspond to panel UI within our application, so you can imagine how the process size of the web application tab grows steadily as the user triggers the showing and hiding of various panels. Our panels are quite large and complex so things sometimes get the point where Safari iOS will reload our web application. Attached to this bug is an HTML file containing a minimal test case that illustrates the issue. Note that there must be at least one image with loading=“lazy” that has never entered the viewport to trigger the retainer issue. There is also a video attached to this bug that demonstrates what we see. I can reproduce this issue in Safari 16.5 as well as WebKit 268505@main. Instructions: Test #1: * Load the test case in a Safari or Webkit build. * Click all of the Remove buttons on the page. * Trigger a garbage collection via the terminal with ‘notifyutil -p org.WebKit.lowMemory’ * Observe that all of the test cases turn green except for the sample that uses images with loading=“lazy” * Click on the “Unparent Children” button for the loading=“lazy” test case * Trigger a garbage collection * Observe that all elements in the subtree turn red, indicating they have been garbage-collected, except for the img elements which remain in memory. Test #2 * Load the test case in a Safari or Webkit build. * Scroll down to the lazy=“loading” test case and scroll through ALL of the images. * Click all of the Remove buttons on the page. * Trigger a garbage collection via the terminal with ‘notifyutil -p org.WebKit.lowMemory’ * Observe that all of the test cases turn green The expectation is that for both tests, all test cases turn green regardless of whether or not the loading=“lazy” img elements have ever entered the viewport.
Created attachment 467913 [details] All-in-One test case that illustrates the img loading="lazy" retainer issue Added an All-in-One test case that illustrates the img loading="lazy" retainer issue
Created attachment 467914 [details] Video demonstrating the img loading="lazy" retainer issue Attaching a sample video demonstrating the img loading="lazy" retainer issue
<rdar://problem/116157415>
One more observation I probably should've mentioned. This retainer issue doesn't happen if the img is a direct descendant of the scrolling element, there has to be an intermediate ancestor between the scrolling element and the img.
It seems to be the same problem reported in https://bugs.webkit.org/show_bug.cgi?id=263521 Are you able to check the proposed solution in that report on Safari? diff --git a/Source/WebCore/html/HTMLImageElement.cpp b/Source/WebCore/html/HTMLImageElement.cpp index 3e4855fd20e2..5ddc923b1568 100644 --- a/Source/WebCore/html/HTMLImageElement.cpp +++ b/Source/WebCore/html/HTMLImageElement.cpp @@ -514,6 +518,8 @@ void HTMLImageElement::removedFromAncestor(RemovalType removalType, ContainerNod selectImageSource(RelevantMutation::Yes); } + m_imageLoader->clearImage(); + HTMLElement::removedFromAncestor(removalType, oldParentOfRemovedTree); FormAssociatedElement::elementRemovedFromAncestor(*this, removalType); } Thanks
*** This bug has been marked as a duplicate of bug 263521 ***