Summary: | [bmalloc] Merging of XLargeRanges can leak the upper range | ||||||||
---|---|---|---|---|---|---|---|---|---|
Product: | WebKit | Reporter: | Zan Dobersek <zan> | ||||||
Component: | bmalloc | Assignee: | Zan Dobersek <zan> | ||||||
Status: | RESOLVED FIXED | ||||||||
Severity: | Normal | CC: | ap, cgarcia, ddkilzer, ggaren, kling, mrobinson, msaboff, webkit-bug-importer | ||||||
Priority: | P2 | Keywords: | InRadar | ||||||
Version: | WebKit Nightly Build | ||||||||
Hardware: | Unspecified | ||||||||
OS: | Unspecified | ||||||||
Attachments: |
|
Description
Zan Dobersek
2016-08-01 07:41:29 PDT
Created attachment 285013 [details]
Patch
Comment on attachment 285013 [details] Patch View in context: https://bugs.webkit.org/attachment.cgi?id=285013&action=review > Source/bmalloc/ChangeLog:18 > + ignored. At least on Linux systems, this resulted in such > + memory allocations being leaked. Can you explain in more detail why we end up with a leak on Linux? On Mac and Windows, at least, it's OK to call vmAllocatePhysicalPages twice for the same address range. > Source/bmalloc/bmalloc/XLargeRange.h:70 > { > - if (a.end() == b.begin()) > + if (a.end() == b.begin() && (a.size() == a.physicalSize() || (!a.physicalSize() && !b.physicalSize()))) > return true; > > - if (b.end() == a.begin()) > + if (b.end() == a.begin() && (b.size() == b.physicalSize() || (!a.physicalSize() && !b.physicalSize()))) > return true; > This will cause serious virtual memory fragmentation, triggering jetsam on iOS. I think I understand what you're reporting now: If we merge(A, B), and B has physicalSize and A doesn't, we produce an XLargeRange with no physicalSize. In principle, this is OK: physicalSize is just an optimization to remember the contiguous committed space at the head of a range, and the optimization has failed since there is none. The problem happens later, in Heap::scavengeLargeObjects. We scavenge by calling XLargeMap::removePhysical -- but the [A, B] range has no physicalSize, so we never scavenge it. I still don't think we want to change the behavior of merge() -- instead, we want to change the behavior of Heap::scavengeLargeObjects. (In reply to comment #2) > Comment on attachment 285013 [details] > Patch > > View in context: > https://bugs.webkit.org/attachment.cgi?id=285013&action=review > > > Source/bmalloc/ChangeLog:18 > > + ignored. At least on Linux systems, this resulted in such > > + memory allocations being leaked. > > Can you explain in more detail why we end up with a leak on Linux? > > On Mac and Windows, at least, it's OK to call vmAllocatePhysicalPages twice > for the same address range. > It's OK on Linux as well. The problem I'm trying to address here is that right-hand XLargeRanges with some physical size can get ignored during merging if the left-hand ranges have partial physical size or no physical size. So the merged range only has information on any physical allocation that was done for the left-hand range, while the allocated memory from the right-hand range doesn't get deallocated during scavenging, but instead relies on some future allocation to reuse that range. (In reply to comment #3) > I think I understand what you're reporting now: > > If we merge(A, B), and B has physicalSize and A doesn't, we produce an > XLargeRange with no physicalSize. > Also if A has physicalSize that's less that its size, which can be the case if A was previously merged from A1 that had physicalSize matching its size, and A2 that had no physicalSize. > In principle, this is OK: physicalSize is just an optimization to remember > the contiguous committed space at the head of a range, and the optimization > has failed since there is none. > > The problem happens later, in Heap::scavengeLargeObjects. We scavenge by > calling XLargeMap::removePhysical -- but the [A, B] range has no > physicalSize, so we never scavenge it. > > I still don't think we want to change the behavior of merge() -- instead, we > want to change the behavior of Heap::scavengeLargeObjects. How exactly? Would it be acceptable to delay merging of ranges until after all the large allocations have been scavenged? > > If we merge(A, B), and B has physicalSize and A doesn't, we produce an
> > XLargeRange with no physicalSize.
> >
>
> Also if A has physicalSize that's less that its size, which can be the case
> if A was previously merged from A1 that had physicalSize matching its size,
> and A2 that had no physicalSize.
If we merge(A, B), and B has no physicalSize and A has physicalSize less than its size, we'll do this:
return XLargeRange(
left.begin(),
a.size() + b.size(),
left.physicalSize());
So, A's physicalSize is not lost.
Created attachment 285252 [details]
Patch
Comment on attachment 285252 [details] Patch View in context: https://bugs.webkit.org/attachment.cgi?id=285252&action=review r=me > Source/bmalloc/ChangeLog:11 > + Recorded physical size is a performance optimization is not the truth, > + so depending on it can cause you to forget physical ranges. Wording seems awkward. Should the second "is" be "and"? The second part of the sentence probably needs some work as well. > Source/bmalloc/ChangeLog:16 > + Be careful each time through the loop to decrement our iterator if > + the map shrunk while we released the lock. Seems awkward as well. Committed r204091: <http://trac.webkit.org/changeset/204091> > > Source/bmalloc/ChangeLog:11 > > + Recorded physical size is a performance optimization is not the truth, > > + so depending on it can cause you to forget physical ranges. > > Wording seems awkward. Should the second "is" be "and"? The second part of > the sentence probably needs some work as well. (bmalloc::Heap::scavengeLargeObjects): Don't use removePhysical(). Recorded physical size is a performance optimization. It is not the truth. So it might be zero even if a range contains physical pages. > > Source/bmalloc/ChangeLog:16 > > + Be careful each time through the loop to decrement our iterator if > > + the map shrunk while we released the lock. > The map can shrink when we release the lock, so we must clamp our iterator each time through the loop. (In reply to comment #5) > > > If we merge(A, B), and B has physicalSize and A doesn't, we produce an > > > XLargeRange with no physicalSize. > > > > > > > Also if A has physicalSize that's less that its size, which can be the case > > if A was previously merged from A1 that had physicalSize matching its size, > > and A2 that had no physicalSize. > > If we merge(A, B), and B has no physicalSize and A has physicalSize less > than its size, we'll do this: > > return XLargeRange( > left.begin(), > a.size() + b.size(), > left.physicalSize()); > > So, A's physicalSize is not lost. Right, I misread your comment. What I was writing about was that if A has some physicalSize that doesn't match its whole size, and B has some physicalSize as well, then B's physicalSize would be lost. Shouldn't be a problem anymore though, thanks for the quick fix. |