Closed
Bug 268513
Opened 21 years ago
Closed 20 years ago
overflow:scroll causes memory growth
Categories
(Core :: CSS Parsing and Computation, defect)
Tracking
()
RESOLVED
FIXED
mozilla1.8beta2
People
(Reporter: yuanyi21, Assigned: yuanyi21)
References
Details
(Keywords: memory-leak)
Attachments
(3 files, 4 obsolete files)
|
1012 bytes,
text/html
|
Details | |
|
150.48 KB,
text/plain
|
Details | |
|
1.31 KB,
patch
|
bzbarsky
:
review+
bzbarsky
:
superreview+
asa
:
approval1.8b2+
|
Details | Diff | Splinter Review |
Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8a5) Gecko/20041104
I'll attach a testcase soon. In that test case, I use java script generate a
large table (5000 cells) and put it in a <div> which have overflow:scroll set in
its style, then I insert this div into another <div> using innerHTML property. I
repeatly doing this every 3 sec and I noticed that the previous data of table is
never freed until I leave that page. On Windows, mozilla's memory usage has
grown from 36M to 86M after 20 iteration. It also happens on all other platform.
This log shows that nsCSSFrameConstructor::BuildScrollFrame is hold 3
additional references to the nsHTMLDivElement which is generated by js and
inserted into another <div>.
At a very quick glance, it looks more like once reference from an nsHashtable
and two from XPConnect. There's probably a bunch of other stuff leaking, so
there's nowhere near sufficient information in that log alone.
I noticed that when create a scrollframe for div, 3 anonymous contents (2 scroll
bars, 1 square) are also created, and added into PresShell's AnonymousContent
hashtable:
http://lxr.mozilla.org/seamonkey/source/layout/html/style/src/nsCSSFrameConstructor.cpp#5427
Those 3 contents can not be discovered by nsContentIterator, therefore they also
can not be removed via nsRange::DeleteContents().
CCing jst who fixed bug 257690 and mentioned in bug 236796 comment 12 that we
are going to "make the iterators aware of anonymous content."
Comment 6•21 years ago
|
||
Hmm.. I would have expected the anonymous content to go away when the frame is
torn down, but it doesn't look like it does. Perhaps we need to
SetAnonymousContentFor(nsnull) on the content node when the primary frame is
destroyed?
That said, anonymous content is cleaned up on presshell destruction. So while
that may be the cause for the runtime memory growth it should not show up in
shutdown leaks... So David is right; if things are leaking on shutdown you need
to find the root leaks.
It sounds like we probably have two separate bugs here...
There is no leak on shutdown in terms of HTMLDivElement. The above reference log
was taken at run time.
Comment 8•21 years ago
|
||
Ah, ok. In that case, chances are you're right. Teaching iterators about
native anonymous content wouldn't really be sufficient, since nodes can be
removed from trees by other means. I think the right solution is to make
teardown of the primary frame tear down the anonymous content involved...
Comment 9•21 years ago
|
||
OK, I tried hacking SetPrimaryFrameFor() to clear anonymous content when it
clears the primary frame.
That didn't help. I also tried opening a new window and closing the one with
the testcase. That also didn't reduce memory usage, even though that definitely
clears out the anonymous content table.
| Assignee | ||
Comment 10•21 years ago
|
||
I created a new method PresShell::ReleaseAnonymousContentFor(nsIContent*) that
does the same thing as SetPrimaryFrameFor()(nsnull) does and removes the content
itself from the HashTable. That successfully released 1 reference, but I have no
idea how to remove the other 2 hold by XPConnect.
| Assignee | ||
Comment 11•21 years ago
|
||
This patch makes XPConnect(WrapNative) add reference to the document, rather
than the div node
(http://lxr.mozilla.org/seamonkey/source/dom/src/base/nsDOMClassInfo.cpp#4973).
But I'm not sure whether it will cause any problem?
RCS file: /cvsroot/mozilla/layout/html/style/src/nsCSSFrameConstructor.cpp,v
retrieving revision 1.1013
diff -u -p -r1.1013 nsCSSFrameConstructor.cpp
--- nsCSSFrameConstructor.cpp 6 Nov 2004 07:07:43 -0000 1.1013
+++ nsCSSFrameConstructor.cpp 10 Nov 2004 10:49:23 -0000
@@ -5433,7 +5433,6 @@ nsCSSFrameConstructor::CreateAnonymousFr
continue;
content->SetNativeAnonymous(PR_TRUE);
- content->SetParent(aParent);
content->SetDocument(aDocument, PR_TRUE, PR_TRUE);
nsresult rv;
@@ -5471,6 +5470,7 @@ nsCSSFrameConstructor::CreateAnonymousFr
// create the frame and attach it to our frame
ConstructFrame(aPresShell, aPresContext, aState, content, aParentFrame,
aChildItems);
}
+ content->SetParent(aParent);
creator->PostCreateFrames();
}
Comment 12•21 years ago
|
||
(In reply to comment #10)
> I created a new method PresShell::ReleaseAnonymousContentFor(nsIContent*)
That's what SetAnonymousContentFor(aContent, nsnull) does, no?
> This patch makes XPConnect(WrapNative) add reference to the document
So in this case the <div> is the _parent_? What's the child? It's clearly a
XUL element if the classinfo code you cite is relevant... Why is this going
through XPConnect at all? (There's the side question of why it's not being torn
down properly, but....)
| Assignee | ||
Comment 13•21 years ago
|
||
(In reply to comment #12)
> That's what SetAnonymousContentFor(aContent, nsnull) does, no?
Not exactly, SetAnonymousContentFor(aContent, nsnull) only clear the anonymous
content for aContent, but it doesn't remove aContent itself from the HashTable
(mAnonymousContentTable) which holding a reference to it.
>
> So in this case the <div> is the _parent_? What's the child? It's clearly a
> XUL element if the classinfo code you cite is relevant... Why is this going
> through XPConnect at all? (There's the side question of why it's not being torn
> down properly, but....)
In the current code, yes, <div> is the parent, its children are 3 XUL element
created in nsCSSFrameConstructor::CreateAnonymousFrame - two scrollbars and one
something else at the right-bottom between those two scrollbars. Seems only
scrollbars added reference to <div>. I have no idea why this is going through
XPConnect.
Comment 14•21 years ago
|
||
> but it doesn't remove aContent itself from the HashTable
Ah, true. That's a bug and should be fixed in SetAnonymousContentFor(). Good
catch!.
The thing in the right-bottom is the scrollcorner.
And I bet the scrollbar XBL bindings are what's causing XPConnect to be
involved. The constructor firing would cause an XPConnect wrapper to be created
for |this| (that being the scrollbar) and that would use the parent (the div)
per that classinfo code you cited.
So why does it use the parent, exactly? And why is this stuff not cleaning
itself up as it should? jst? dbradley? Any idea?
Comment 15•21 years ago
|
||
Any XUL elements we use the parent object as the scope for the JS wrapper, which
means if the XUL elements that makes up the scrollbar ends up being a child of a
HTML div, the wrapper for the XUL element in the scrollbar will keep the div
alive as long as the XUL element is alive... If things go away when you leave
the page (or shortly there after, wait at least 2 seconds), then everything is
cleaned up properly, we're just not GC'ing often enough in this case, it seems...
Comment 16•21 years ago
|
||
> If things go away when you leave the page (or shortly there after, wait at
> least 2 seconds)
In my testing, they don't seem to. At least the memory usage does not drop. I
opened and closed several windows and loaded about:mozilla, about:, etc several
times to test.
| Assignee | ||
Comment 17•21 years ago
|
||
jst, is there any problem if I use the document as the scope for the JS wrapper?
That makes more sense to me because those JS wrappers only go away when the
document was gone.
Summary: overflow:scroll causes memory leak → overflow:scroll causes memory growth
| Assignee | ||
Comment 18•21 years ago
|
||
I'm sure that nsHTMLScrollFrame::Destroy() is not the correct place to put the
fix, but anyway, just want to make sure I'm on the right way.
| Assignee | ||
Comment 19•21 years ago
|
||
Comment on attachment 166081 [details] [diff] [review]
first try
bz, jst, any comment?
Comment 20•21 years ago
|
||
I think the right place to remove the anonymous content is where we set the
primary frame to null...
| Assignee | ||
Comment 21•21 years ago
|
||
(In reply to comment #20)
> I think the right place to remove the anonymous content is where we set the
> primary frame to null...
Can you be more specific? where do we set the primary frame to null, exactly?
Comment 22•21 years ago
|
||
Well, the most obvious place is inside SetPrimaryFrameFor...
But I just realized that it's possible for a frame to get created, create its
anonymous content, and get destroyed all without being set as the primary frame
(eg in some sort of {ib} situation or whatnot). So what we _really_ want to do
is to clear anonymous content when an nsIAnonymousContentCreator frame dies.
But I'm not sure how that fits with XTF, and I'm also not sure whether it's
possible to have multiple nsIAnonymousContentCreator frames per single content
node (the SetAnonymousContentFor code sure seems to support that).
In brief, we need to document what our invariants are here, at the very least. :(
| Assignee | ||
Comment 23•20 years ago
|
||
The change in nsPresShell.cpp is to a) release 1 reference count of
XPCWrappedNative object so that it can be GCed later; b) clear the content from
|mAnonymousContentTable| hash table.
The change in nsFrame.cpp is to call PresShell::SetAnonymousContentFor when a
nsIAnonymousContentCreator-derived frame is going to be destroyed.
Attachment #166081 -
Attachment is obsolete: true
Attachment #169358 -
Flags: review?(jst)
Comment 24•20 years ago
|
||
Comment on attachment 169358 [details] [diff] [review]
v0.2
- In nsFrame::Destroy():
+ nsCOMPtr<nsIAnonymousContentCreator> isAnonymousContentCreator =
do_QueryInterface(NS_STATIC_CAST(nsIFrame*, this));
+ if (isAnonymousContentCreator) {
+ shell->SetAnonymousContentFor(mContent, nsnull);
+ }
It'd be cool if there was a cheaper way to do this than a QI to
nsIAnonymousContentCreator. Maybe we need to add a destructor to that interface
and put this code there?
dbaron should have a look, but r=jst either way.
Attachment #169358 -
Flags: superreview?(dbaron)
Attachment #169358 -
Flags: review?(jst)
Attachment #169358 -
Flags: review+
Comment on attachment 169358 [details] [diff] [review]
v0.2
>Index: layout/base/nsPresShell.cpp
>+ nsCOMPtr<nsISupports> old_ref = mDocument->RemoveReference(content);
I don't understand why this belongs here. Why should this happen only for
anonymous content? The only other time we call RemoveReference is when moving
content between documents via insertBefore or replaceChild.
>+ {
>+ nsCOMPtr<nsIAnonymousContentCreator> isAnonymousContentCreator = do_QueryInterface(NS_STATIC_CAST(nsIFrame*, this));
No need for nsCOMPtr. Just use a raw pointer, e.g.:
nsIAnonymousContentCreator *creator;
if (NS_SUCCEEDED(CallQueryInterface(mContent, &creator))
...
(And don't call a pointer variable |is...|, even if you're just null-checking
it.)
Comment on attachment 169358 [details] [diff] [review]
v0.2
I meant this rather than mContent.
But also, nsIAnonymousContentCreator isn't the only source of anonymous content
maintained in the pres shell -- :before and :after content are also tracked.
Shouldn't we clear that too? Maybe it should just happen in
nsPresShell::NotifyDestroyingFrame, with no nsIAnonymousContentCreator check?
Attachment #169358 -
Flags: superreview?(dbaron) → superreview-
| Assignee | ||
Comment 27•20 years ago
|
||
(In reply to comment #25)
> (From update of attachment 169358 [details] [diff] [review] [edit])
> >Index: layout/base/nsPresShell.cpp
> >+ nsCOMPtr<nsISupports> old_ref = mDocument->RemoveReference(content);
>
> I don't understand why this belongs here. Why should this happen only for
> anonymous content? The only other time we call RemoveReference is when moving
> content between documents via insertBefore or replaceChild.
This is because when an anonymous content created in
nsCSSFrameConstructor::CreateAnonymousFrames(), if it's a xpconnect wrapped
content, nsDocument::AddReference will be called for adding both the anonymous
content and its XPCWrappedNative object to the document. (see
http://lxr.mozilla.org/seamonkey/source/content/xbl/src/nsXBLProtoImpl.cpp#137,
the full code path can be found in attachment 165249 [details])
Currently, we only release those XPCWrappedNative objects in nsDocument's dtor
(through mContentWrapperHash's dtor). Here, the explicit call of RemoveReference
ensures releasing one reference count of XPCWrappedNative so that it can be
GCed later instead of living in memory until the document destroyed.
| Assignee | ||
Comment 28•20 years ago
|
||
Moved SetAnonymousContentFor from nsFrame::Destroy to
PresShell::NotifyDestroyingFrame despite whether the frame is an anonymous
content creator or not. (Is there any performance impact for doing this?)
Attachment #169358 -
Attachment is obsolete: true
Attachment #169622 -
Flags: review?(dbaron)
comment 27 doesn't answer the question: why should this happen *only* for
anonymous content?
| Assignee | ||
Comment 30•20 years ago
|
||
nsDocument::AddReference is called when
nsCSSFrameConstructor::CreateAnonymousFrames() is generating the anonymous
content. I'm just trying to do the reverse thing. There are probably some other
place called nsDocument::AddReference, but they are not releated to this memory
growth.
Comment 31•20 years ago
|
||
As for calling mDocument->RemoveReference() for anonymous content when its
frames are destroyed that makes sense to me. The only reason for the document
holding a reference to the node here is to prevent it from being garbage
collected when all references to it are lost, and here the frame is gone, and
this node won't be reachable in any way any more unless code already has
references to it, in which case it won't be garbage collected etc etc. So that
part seems fine with me.
Comment on attachment 169622 [details] [diff] [review]
v0.3
ok, r=dbaron (or sr). I understand nsDocument::RemoveReference now (and it's
evil, and needs to be removed eventually), but this will fix a leak in the case
where we can fix it without breaking anything (which really isn't the best
place, but it's the best we can do without breaking stuff).
Attachment #169622 -
Flags: review?(dbaron) → review+
Attachment #169622 -
Flags: superreview?(jst)
| Assignee | ||
Comment 33•20 years ago
|
||
taking, target on 1.9 alpha.
Assignee: dbaron → kyle.yuan
Target Milestone: --- → mozilla1.9alpha
This probably could go in for 1.8beta2.
| Assignee | ||
Comment 35•20 years ago
|
||
that will be great.
Comment 36•20 years ago
|
||
Comment on attachment 169622 [details] [diff] [review]
v0.3
sr=jst
Attachment #169622 -
Flags: superreview?(jst)
Attachment #169622 -
Flags: superreview+
Attachment #169622 -
Flags: approval1.8b?
Comment 37•20 years ago
|
||
Comment on attachment 169622 [details] [diff] [review]
v0.3
a-, too late.
Attachment #169622 -
Flags: approval1.8b? → approval1.8b-
| Assignee | ||
Comment 38•20 years ago
|
||
fixed in 1.8 beta2.
Checking in layout/base/nsPresShell.cpp;
/cvsroot/mozilla/layout/base/nsPresShell.cpp,v <-- nsPresShell.cpp
new revision: 3.813; previous revision: 3.812
Status: NEW → RESOLVED
Closed: 20 years ago
Resolution: --- → FIXED
Target Milestone: mozilla1.9alpha → mozilla1.8beta2
Comment 39•20 years ago
|
||
I think this fix has caused bug 283611.
Comment 40•20 years ago
|
||
It also caused bug 283863
Comment 41•20 years ago
|
||
It also caused bug 283788.
Comment 42•20 years ago
|
||
This also caused bug 283997. See bug 283611 comment 1 for my guess as to what's
wrong with this patch...
Depends on: 283997
| Assignee | ||
Comment 43•20 years ago
|
||
I've backed out the fix.
Status: RESOLVED → REOPENED
Resolution: FIXED → ---
Comment 44•20 years ago
|
||
My bug 284041, which I described nearly the same as 283863, is STILL PRESENT on
today's Trunk Build (Linux, GTK1, Classic Theme).
Only difference between my bug and 283863 is that I can successfully make out
the edges of the scrollbar--- barely. And I've got a really good monitor.
Comment 45•20 years ago
|
||
I tried to add "mlk" keyword, but it would not let me. Can someone else add this?
Comment 46•20 years ago
|
||
It may be that one "right" place to clear the anonymous content is when the node
is removed from the document (see nsBindingManager::ChangeDocumentFor). The
patch in bug 286000 may well end up doing that if it hasn't happened by that time.
That would fix the testcase in this bug, I suspect...
Depends on: 286000
Comment 47•20 years ago
|
||
So with the patch for bug 286000 in, do we still get memory growth on the
testcase with just the PresShell::SetAnonymousContentFor part of the patch for
this bug? If not, I think checking that in is the way to go...
| Assignee | ||
Comment 48•20 years ago
|
||
bz, I'll check it out today.
| Assignee | ||
Comment 49•20 years ago
|
||
Attachment #169622 -
Attachment is obsolete: true
Attachment #179932 -
Flags: superreview?(bzbarsky)
Attachment #179932 -
Flags: review?(bzbarsky)
| Assignee | ||
Comment 50•20 years ago
|
||
Comment on attachment 179932 [details] [diff] [review]
with 286000 fixed, only need 1-line to fix the memory growth
oops, this will cause a crash due to removing an item during enumeration of
hashtable.
Attachment #179932 -
Attachment is obsolete: true
Attachment #179932 -
Flags: superreview?(bzbarsky)
Attachment #179932 -
Flags: review?(bzbarsky)
| Assignee | ||
Comment 51•20 years ago
|
||
Attachment #179935 -
Flags: review?(bzbarsky)
Comment 52•20 years ago
|
||
Comment on attachment 179935 [details] [diff] [review]
a few more code to prevent the crash
I guess this works for now, though we should really just eliminate
ReleaseAnonymousContent() as an api method, and then we can just use the
mIsDestroying member for this check....
Attachment #179935 -
Flags: superreview+
Attachment #179935 -
Flags: review?(bzbarsky)
Attachment #179935 -
Flags: review+
Attachment #179935 -
Flags: approval-aviary1.1a?
Comment 53•20 years ago
|
||
Comment on attachment 179935 [details] [diff] [review]
a few more code to prevent the crash
a=asa for checkin to the 1.8b2 trunk
Attachment #179935 -
Flags: approval-aviary1.1a? → approval1.8b2+
The additional change to prevent a crash seems wrong, in that it fixes the crash
by leaking in the case where it fixes the crash. Wouldn't a better thing be to
ensure that the releases happen after the Remove by holding a reference?
| Assignee | ||
Comment 55•20 years ago
|
||
David, we do not need to call mAnonymousContentTable->Remove during
PresShell::ReleaseAnonymousContent, because the destruction of
mAnonymousContentTable will remove all keys anyway.
I just landed the patch without seeing your comment. Please let me know if the
patch needs further change.
Status: REOPENED → RESOLVED
Closed: 20 years ago → 20 years ago
Resolution: --- → FIXED
Sorry, never mind. I was thinking you were doing all the boolean twiddling for
a different bug.
You need to log in
before you can comment on or make changes to this bug.
Description
•