New issue
Advanced search Search tips
Note: Color blocks (like or ) mean that a user may not be available. Tooltip shows the reason.

Issue 707544 link

Starred by 90 users

Issue metadata

Status: Fixed
Owner:
Closed: Apr 2017
Cc:
Components:
EstimatedDays: ----
NextAction: ----
OS: Linux , Windows , Mac
Pri: 1
Type: Bug-Regression


Show other hotlists

Hotlists containing this issue:
Top-Starred-Bugs


Sign in to add a comment

Process memory continues to increase when recycling a large number of DOM elements

Reported by p.m.g.me...@gmail.com, Apr 1 2017

Issue description

UserAgent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/57.0.2987.133 Safari/537.36

Steps to reproduce the problem:
1. Open attached html in a new Chrome tab
2. Open Task Manager
3. Observe tab's process memory value going up

What is the expected behavior?
Have the process memory being freed just like is observed when doing the same evaluation in the previous Chrome stable 56.0.2924.87.

What went wrong?
Process memory is not being freed. The memory continues going up until the tab is killed.

Element#innerHTML call should make the child elements to be garbage collected at some point. But it doesn't.

Did this work before? Yes 56.0.2924.87

Does this work in other browsers? Yes

Chrome version: 57.0.2987.133  Channel: stable
OS Version: 7
Flash Version:
 
chrome-57-memory-leak-test-jquery-html.html
2.3 KB View Download
Components: Blink>MemoryAllocator>GarbageCollection Blink>HTML Blink>DOM
Labels: -Pri-2 Performance-Memory Stability-Memory Pri-1
Status: Untriaged (was: Unconfirmed)
Reproduced on Windows 7 and Chrome 57.
Adding DOM and HTML since it might be .append() or .innerHTML, adding garbage collector since it might be Oilpan.
Also reproducible in Windos Server 2008, Linux debian based distros - Ubuntu and Linux Mint - and Mac OS X.
Labels: OS-Linux OS-Mac
Also reproducible on Suse Leap 4.2

Comment 5 by m...@bitovi.com, Apr 5 2017

The original attached file (chrome-57-memory-leak-test-jquery-html.html) is using jQuery's .append().  

The one attached here (chrome57-leak.html) is a much more simplified version of what jQuery's .append is doing, and it uses pure DOM instead of jQuery.

The key is the line `elem.getElementsByTagName('script' || '*');`. Removing that gets rid of the leak.
chrome57-leak.html
545 bytes View Download

Comment 6 by phistuck@gmail.com, Apr 5 2017

That document fragment is causing many memory leaks across various contexts. :(
(Or two ;))

Comment 7 by tkent@chromium.org, Apr 6 2017

Labels: Needs-Bisect
Sounds like cache for getElementsByTagName() isn't collected.

Comment 8 by kochi@chromium.org, Apr 7 2017

Components: Blink>JavaScript
Labels: -Needs-Bisect
I tried bisect-builds.py and found that the range is between 439853 (known good)
and 439866 (known bad).
This is same as issue 701601, so this is quite probably the issue with
introducing TraceWrapper.
The difference is, that the fix for 701601 should be in for M58 and M59,
but this issue still reproduces on ToT build.
Adding Blink>Javascript.

Comment 9 by tkent@chromium.org, Apr 7 2017

Labels: ReleaseBlock-Stable M-58
Attaching my accelerated version of the repro script (original one is in
comment#5).
This replaces span with <input> (which uses more memory per node) and
repeat in 0.1sec (originally 1sec).
chrome57-leak.html
545 bytes View Download
Owner: mlippautz@chromium.org
Status: Assigned (was: Untriaged)
Michael, could you take a look?
 Issue 609137  may be another instance of this. I suggest you write a C++ version of this in a unit test and see if you can cause a leak without JavaScript.
Cc: mlippautz@chromium.org
Owner: keishi@chromium.org
Keishi-san, could you take a stab at this? This looks like more of an
Oilpan issue, rather than V8's GC.
Cc: tjsavage@chromium.org dominicc@chromium.org
Adding people interested in this issue in cc list.
Owner: mlippautz@chromium.org
Looks like a wrapper tracing issue. Assigning to mlippautz

I was able to reduce the test down to https://codereview.chromium.org/2805813003

The problem seems to be that live HTMLCollections is leaking.
According to devtools memory panel the retainer is HTMLDocument.
I don't know how trace wrapper works but I am suspecting that somehow Document::m_nodeLists's WeakMembers are traced as strong references in traceWrapper, which retains HTMLCollection wrapper, which retains HTMLCollection, which means it doesn't get removed from Document::m_nodeLists.
Thanks for the analysis, I'll follow up as soon as I have some cycles.
Cc: jochen@chromium.org
Keishi, your analysis looks correct. Wrapper tracing only knows about strong references, and we definitely trace Document::m_nodeLists [1]

haraken,jochen: Can you provide some guidance here? We usually try to avoid tracing references that are only held weakly in Oilpan, but this one was in before I started working on the project, so I guess there was a reason to do so.

[1]: https://cs.chromium.org/chromium/src/third_party/WebKit/Source/core/dom/Document.cpp?q=Document.cpp&sq=package:chromium&dr&l=6592
Cc: keishi@chromium.org haraken@chromium.org
Looks like the tracing was added in https://chromium.googlesource.com/chromium/src/+/a43e2359d03497a2a8a7e8327470abcf18eaa7d0

But I don't see how we would want to trace Document::m_nodeLists. They aren't JS exposed.

Could we just delete it like
https://codereview.chromium.org/2803313003

+haraken
The following tests failed when we don't trace Document::m_nodeLists
fast/dom/NodeList/nodelist-reachable.html
fast/dom/gc-9.html
fast/dom/htmlallcollection-reachable.html
fast/dom/htmlcollection-reachable.html

I can't find it written in the spec but it looks like we need to keep the LiveNodeList wrappers alive as long as the LiveNodeList::rootNode() element is alive.

I think the LiveNodeList::rootNode() should retain the LiveNodeList, and the LiveNodeList should retain its wrapper. But I can't figure out how to express that in TraceWrapper.
Cc: sigbjo...@opera.com
Yes. If we call:

  node.getElementsByTagName();

then we need to keep alive the node list while the node is alive. In other words, we need to trace from the node to the node list (*).

The problem is that we don't have a trivial reference from the node to the node list. That's why we're (intentionally) doing something tricky; i.e., strongly trace Document::m_nodeLists.

I'm a bit confused but is the node list really leaking? If we want to realize the behavior (*), we need to keep alive the node list while the node is alive. It's expected that the node list doesn't get collected until the node gets collected.

Another option would be to change the behavior (*). How do other browsers behave?


Looks like Firefox does not leak. Also Chrome didn't leak either prior to trace wrapper.
Would it be possible to instead have a weak callback (on Document) magically do some incremental wrapper tracing of the live NodeLists?
Let me ask the probably naive question here: Since m_nodeLists are weak for Oilpan anyways, what prohibits us from having a vector of wrapper tracing references on the actual nodes pointing to the corresponding node lists?

Ideally we would converge to a solution where the object graph is properly set up so regular parent/child relationships determine liveness.
#24: That's a very good question.

Sigbjorn: Do you know why we don't have a HeapMember<LiveNodeList> on NodeRareData? Then we can simply trace the node list without relying on the weak member on Document.

Comment 26 by tkent@chromium.org, Apr 10 2017

I think NodeLists are already owned by NodeListsNodeData, which is a part of NodeRareData.
Document::node_lists_ is used for DOMTreeVersion invalidation.
I now understand that it is wrong to wrapper-trace the node list from the document. If we do that, we end up with keeping the node list alive as long as the document is alive, not the node is alive. (i.e., even if we remove the node, the node list will be kept alive as long as the document is alive.) This leaks memory.

So, yes, we should wrapper-trace the node list from the node.

> I think NodeLists are already owned by NodeListsNodeData, which is a part of NodeRareData.

Thanks. Yeah, it makes more sense to trace NodeListsNodeData::child_node_list_.

By the way, do you know why NodeListsNodeData::child_node_list_ are weak members?


Do we already wrapper trace the WeakMember NodeListsNodeData::child_node_list_ as strong?
https://cs.chromium.org/chromium/src/third_party/WebKit/Source/core/dom/NodeListsNodeData.cpp?rcl=f3e971b60293bb5f7e6bd8de03b7f3ea77a2be13&l=53
Yes we trace in NodeListsNodeData and as far as I understand it this should be enough. I would add some debugging information and check why we need to trace the node lists on the document.

I am not sure I will get to this issue this week though as we have a short week here (holiday) and I have other non-arguable deadlines coming up.
> Yes we trace in NodeListsNodeData and as far as I understand it this should be enough. I would add some debugging information and check why we need to trace the node lists on the document.

Sounds good to me!

Quick update: I had a look and we indeed seem to hold unnecessary HTMLCollection and LiveNodeList alive. The question now is how to avoid tracing through Document but rather trace through the Node.

NodeListsNodeData::child_node_list_ is just the list of children (as the name suggests). We have two more cashes though in NodeListsNodeData which are atomic_name_caches_ and tag_collection_cache_ns_. Will now have a look at those.
The problem seems to be indeed that we only trace wrappers for a subset of the lists on NodeListsNodeData. Potentialy fix is in  https://codereview.chromium.org/2809183003/

- I checked against the first two tests that were failing and they pass.
- I also checked against the minimized example. We hover somewhere around 40M of JS memory, depending on GC timing.
Status: Started (was: Assigned)
Labels: Merge-Request-58
Status: Fixed (was: Started)
This should be fixed now and merged back to M58 once backed into Canary.
Project Member

Comment 36 by sheriffbot@chromium.org, Apr 13 2017

Labels: -Merge-Request-58 Hotlist-Merge-Approved Merge-Approved-58
Your change meets the bar and is auto-approved for M58. Please go ahead and merge the CL to branch 3029 manually. Please contact milestone owner if you have questions.
Owners: amineer@(Android), cmasso@(iOS), bhthompson@(ChromeOS), govind@(Desktop)

For more details visit https://www.chromium.org/issue-tracking/autotriage - Your friendly Sheriffbot
How can I identify the M58 snapshots containing this fix?
https://storage.googleapis.com/chromium-browser-snapshots/index.html?prefix=Win/
Project Member

Comment 38 by bugdroid1@chromium.org, Apr 13 2017

Labels: -merge-approved-58 merge-merged-3029
The following revision refers to this bug:
  https://chromium.googlesource.com/chromium/src.git/+/fb5051f1e67383a98cf36b21f7cf78a9a44e6301

commit fb5051f1e67383a98cf36b21f7cf78a9a44e6301
Author: Michael Lippautz <mlippautz@chromium.org>
Date: Thu Apr 13 11:34:41 2017

[wrapper-tracing] Fix node list leak

Only trace node lists that hang of live nodes.

BUG= chromium:707544 

Review-Url: https://codereview.chromium.org/2809183003
Cr-Commit-Position: refs/heads/master@{#463986}
(cherry picked from commit 76798dbd04a30a8e1acdf71ecab93d09dac4ec5b)

Review-Url: https://codereview.chromium.org/2810253003 .
Cr-Commit-Position: refs/branch-heads/3029@{#690}
Cr-Branched-From: 939b32ee5ba05c396eef3fd992822fcca9a2e262-refs/heads/master@{#454471}

[modify] https://crrev.com/fb5051f1e67383a98cf36b21f7cf78a9a44e6301/third_party/WebKit/Source/core/dom/Document.cpp
[modify] https://crrev.com/fb5051f1e67383a98cf36b21f7cf78a9a44e6301/third_party/WebKit/Source/core/dom/NodeListsNodeData.cpp
[modify] https://crrev.com/fb5051f1e67383a98cf36b21f7cf78a9a44e6301/third_party/WebKit/Source/core/dom/NodeListsNodeData.h

Comment 39 Deleted

Comment 40 by dodz...@gmail.com, Apr 13 2017

"How can I identify the M58 snapshots containing this fix?
https://storage.googleapis.com/chromium-browser-snapshots/index.html?prefix=Win/"

LAST_CHANGE -> 464386
 Issue 711448  has been merged into this issue.
Labels: TE-Verified-M58 TE-Verified-58.0.3029.81
Tested the issue on windows 7, Ubuntu 14.04 and Mac 10.12.3 using chrome version 58.0.3029.81 with the below steps

1.Opened the attached chrome57-leak.html from comment #10.
2.Opened task manager
3.Observed memory increases and again freed which is not observed in M57.

Please find the attached screen cast for the reference.
Adding TE-Verified labels.

Thanks,
707544.mp4
939 KB View Download
Cc: yhirano@chromium.org jmukthavaram@chromium.org tkent@chromium.org
 Issue 704546  has been merged into this issue.

Comment 44 by w...@chromium.org, Apr 19 2017

This was reported in M57 - should we merge it back there too?

Comment 45 by phistuck@gmail.com, Apr 19 2017

#44 - Chrome 58 is a few days away, so Chrome 57 will not go to the public at this point.

Sign in to add a comment