2 * Copyright (C) 2011 Apple Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 #include "CopiedSpace.h"
29 #include "CopiedSpaceInlines.h"
30 #include "GCActivityCallback.h"
31 #include "JSCInlines.h"
36 CopiedSpace::CopiedSpace(Heap
* heap
)
38 , m_inCopyingPhase(false)
39 , m_shouldDoCopyPhase(false)
40 , m_numberOfLoanedBlocks(0)
41 , m_bytesRemovedFromOldSpaceDueToReallocation(0)
45 CopiedSpace::~CopiedSpace()
47 while (!m_oldGen
.toSpace
->isEmpty())
48 CopiedBlock::destroy(m_oldGen
.toSpace
->removeHead());
50 while (!m_oldGen
.fromSpace
->isEmpty())
51 CopiedBlock::destroy(m_oldGen
.fromSpace
->removeHead());
53 while (!m_oldGen
.oversizeBlocks
.isEmpty())
54 CopiedBlock::destroy(m_oldGen
.oversizeBlocks
.removeHead());
56 while (!m_newGen
.toSpace
->isEmpty())
57 CopiedBlock::destroy(m_newGen
.toSpace
->removeHead());
59 while (!m_newGen
.fromSpace
->isEmpty())
60 CopiedBlock::destroy(m_newGen
.fromSpace
->removeHead());
62 while (!m_newGen
.oversizeBlocks
.isEmpty())
63 CopiedBlock::destroy(m_newGen
.oversizeBlocks
.removeHead());
65 ASSERT(m_oldGen
.toSpace
->isEmpty());
66 ASSERT(m_oldGen
.fromSpace
->isEmpty());
67 ASSERT(m_oldGen
.oversizeBlocks
.isEmpty());
68 ASSERT(m_newGen
.toSpace
->isEmpty());
69 ASSERT(m_newGen
.fromSpace
->isEmpty());
70 ASSERT(m_newGen
.oversizeBlocks
.isEmpty());
73 void CopiedSpace::init()
75 m_oldGen
.toSpace
= &m_oldGen
.blocks1
;
76 m_oldGen
.fromSpace
= &m_oldGen
.blocks2
;
78 m_newGen
.toSpace
= &m_newGen
.blocks1
;
79 m_newGen
.fromSpace
= &m_newGen
.blocks2
;
84 CheckedBoolean
CopiedSpace::tryAllocateSlowCase(size_t bytes
, void** outPtr
)
86 if (isOversize(bytes
))
87 return tryAllocateOversize(bytes
, outPtr
);
89 ASSERT(m_heap
->vm()->currentThreadIsHoldingAPILock());
90 m_heap
->didAllocate(m_allocator
.currentCapacity());
94 *outPtr
= m_allocator
.forceAllocate(bytes
);
98 CheckedBoolean
CopiedSpace::tryAllocateOversize(size_t bytes
, void** outPtr
)
100 ASSERT(isOversize(bytes
));
102 CopiedBlock
* block
= CopiedBlock::create(WTF::roundUpToMultipleOf
<sizeof(double)>(sizeof(CopiedBlock
) + bytes
));
103 m_newGen
.oversizeBlocks
.push(block
);
104 m_newGen
.blockFilter
.add(reinterpret_cast<Bits
>(block
));
105 m_blockSet
.add(block
);
106 ASSERT(!block
->isOld());
108 CopiedAllocator allocator
;
109 allocator
.setCurrentBlock(block
);
110 *outPtr
= allocator
.forceAllocate(bytes
);
111 allocator
.resetCurrentBlock();
113 m_heap
->didAllocate(block
->capacity());
118 CheckedBoolean
CopiedSpace::tryReallocate(void** ptr
, size_t oldSize
, size_t newSize
)
120 if (oldSize
>= newSize
)
124 ASSERT(!m_heap
->vm()->isInitializingObject());
126 if (CopiedSpace::blockFor(oldPtr
)->isOversize() || isOversize(newSize
))
127 return tryReallocateOversize(ptr
, oldSize
, newSize
);
129 if (m_allocator
.tryReallocate(oldPtr
, oldSize
, newSize
))
133 if (!tryAllocate(newSize
, &result
)) {
137 memcpy(result
, oldPtr
, oldSize
);
142 CheckedBoolean
CopiedSpace::tryReallocateOversize(void** ptr
, size_t oldSize
, size_t newSize
)
144 ASSERT(isOversize(oldSize
) || isOversize(newSize
));
145 ASSERT(newSize
> oldSize
);
150 if (!tryAllocateOversize(newSize
, &newPtr
)) {
155 memcpy(newPtr
, oldPtr
, oldSize
);
157 CopiedBlock
* oldBlock
= CopiedSpace::blockFor(oldPtr
);
158 if (oldBlock
->isOversize()) {
159 // FIXME: Eagerly deallocating the old space block probably buys more confusion than
161 // https://bugs.webkit.org/show_bug.cgi?id=144750
162 if (oldBlock
->isOld()) {
163 m_bytesRemovedFromOldSpaceDueToReallocation
+= oldBlock
->size();
164 m_oldGen
.oversizeBlocks
.remove(oldBlock
);
166 m_newGen
.oversizeBlocks
.remove(oldBlock
);
167 m_blockSet
.remove(oldBlock
);
168 CopiedBlock::destroy(oldBlock
);
175 void CopiedSpace::doneFillingBlock(CopiedBlock
* block
, CopiedBlock
** exchange
)
177 ASSERT(m_inCopyingPhase
);
180 *exchange
= allocateBlockForCopyingPhase();
185 if (!block
->dataSize()) {
186 recycleBorrowedBlock(block
);
190 block
->zeroFillWilderness();
193 // Always put the block into the old gen because it's being promoted!
194 SpinLockHolder
locker(&m_toSpaceLock
);
195 m_oldGen
.toSpace
->push(block
);
196 m_blockSet
.add(block
);
197 m_oldGen
.blockFilter
.add(reinterpret_cast<Bits
>(block
));
201 MutexLocker
locker(m_loanedBlocksLock
);
202 ASSERT(m_numberOfLoanedBlocks
> 0);
203 ASSERT(m_inCopyingPhase
);
204 m_numberOfLoanedBlocks
--;
205 if (!m_numberOfLoanedBlocks
)
206 m_loanedBlocksCondition
.signal();
210 void CopiedSpace::didStartFullCollection()
212 ASSERT(heap()->operationInProgress() == FullCollection
);
213 ASSERT(m_oldGen
.fromSpace
->isEmpty());
214 ASSERT(m_newGen
.fromSpace
->isEmpty());
217 for (CopiedBlock
* block
= m_newGen
.toSpace
->head(); block
; block
= block
->next())
218 ASSERT(!block
->liveBytes());
220 for (CopiedBlock
* block
= m_newGen
.oversizeBlocks
.head(); block
; block
= block
->next())
221 ASSERT(!block
->liveBytes());
224 for (CopiedBlock
* block
= m_oldGen
.toSpace
->head(); block
; block
= block
->next())
225 block
->didSurviveGC();
227 for (CopiedBlock
* block
= m_oldGen
.oversizeBlocks
.head(); block
; block
= block
->next())
228 block
->didSurviveGC();
231 void CopiedSpace::doneCopying()
234 MutexLocker
locker(m_loanedBlocksLock
);
235 while (m_numberOfLoanedBlocks
> 0)
236 m_loanedBlocksCondition
.wait(m_loanedBlocksLock
);
239 ASSERT(m_inCopyingPhase
== m_shouldDoCopyPhase
);
240 m_inCopyingPhase
= false;
242 DoublyLinkedList
<CopiedBlock
>* toSpace
;
243 DoublyLinkedList
<CopiedBlock
>* fromSpace
;
244 TinyBloomFilter
* blockFilter
;
245 if (heap()->operationInProgress() == FullCollection
) {
246 toSpace
= m_oldGen
.toSpace
;
247 fromSpace
= m_oldGen
.fromSpace
;
248 blockFilter
= &m_oldGen
.blockFilter
;
250 toSpace
= m_newGen
.toSpace
;
251 fromSpace
= m_newGen
.fromSpace
;
252 blockFilter
= &m_newGen
.blockFilter
;
255 while (!fromSpace
->isEmpty()) {
256 CopiedBlock
* block
= fromSpace
->removeHead();
257 // We don't add the block to the blockSet because it was never removed.
258 ASSERT(m_blockSet
.contains(block
));
259 blockFilter
->add(reinterpret_cast<Bits
>(block
));
260 block
->didSurviveGC();
261 toSpace
->push(block
);
264 if (heap()->operationInProgress() == EdenCollection
) {
265 m_oldGen
.toSpace
->append(*m_newGen
.toSpace
);
266 m_oldGen
.oversizeBlocks
.append(m_newGen
.oversizeBlocks
);
267 m_oldGen
.blockFilter
.add(m_newGen
.blockFilter
);
268 m_newGen
.blockFilter
.reset();
271 ASSERT(m_newGen
.toSpace
->isEmpty());
272 ASSERT(m_newGen
.fromSpace
->isEmpty());
273 ASSERT(m_newGen
.oversizeBlocks
.isEmpty());
277 m_shouldDoCopyPhase
= false;
280 size_t CopiedSpace::size()
282 size_t calculatedSize
= 0;
284 for (CopiedBlock
* block
= m_oldGen
.toSpace
->head(); block
; block
= block
->next())
285 calculatedSize
+= block
->size();
287 for (CopiedBlock
* block
= m_oldGen
.fromSpace
->head(); block
; block
= block
->next())
288 calculatedSize
+= block
->size();
290 for (CopiedBlock
* block
= m_oldGen
.oversizeBlocks
.head(); block
; block
= block
->next())
291 calculatedSize
+= block
->size();
293 for (CopiedBlock
* block
= m_newGen
.toSpace
->head(); block
; block
= block
->next())
294 calculatedSize
+= block
->size();
296 for (CopiedBlock
* block
= m_newGen
.fromSpace
->head(); block
; block
= block
->next())
297 calculatedSize
+= block
->size();
299 for (CopiedBlock
* block
= m_newGen
.oversizeBlocks
.head(); block
; block
= block
->next())
300 calculatedSize
+= block
->size();
302 return calculatedSize
;
305 size_t CopiedSpace::capacity()
307 size_t calculatedCapacity
= 0;
309 for (CopiedBlock
* block
= m_oldGen
.toSpace
->head(); block
; block
= block
->next())
310 calculatedCapacity
+= block
->capacity();
312 for (CopiedBlock
* block
= m_oldGen
.fromSpace
->head(); block
; block
= block
->next())
313 calculatedCapacity
+= block
->capacity();
315 for (CopiedBlock
* block
= m_oldGen
.oversizeBlocks
.head(); block
; block
= block
->next())
316 calculatedCapacity
+= block
->capacity();
318 for (CopiedBlock
* block
= m_newGen
.toSpace
->head(); block
; block
= block
->next())
319 calculatedCapacity
+= block
->capacity();
321 for (CopiedBlock
* block
= m_newGen
.fromSpace
->head(); block
; block
= block
->next())
322 calculatedCapacity
+= block
->capacity();
324 for (CopiedBlock
* block
= m_newGen
.oversizeBlocks
.head(); block
; block
= block
->next())
325 calculatedCapacity
+= block
->capacity();
327 return calculatedCapacity
;
330 static bool isBlockListPagedOut(double deadline
, DoublyLinkedList
<CopiedBlock
>* list
)
332 unsigned itersSinceLastTimeCheck
= 0;
333 CopiedBlock
* current
= list
->head();
335 current
= current
->next();
336 ++itersSinceLastTimeCheck
;
337 if (itersSinceLastTimeCheck
>= Heap::s_timeCheckResolution
) {
338 double currentTime
= WTF::monotonicallyIncreasingTime();
339 if (currentTime
> deadline
)
341 itersSinceLastTimeCheck
= 0;
348 bool CopiedSpace::isPagedOut(double deadline
)
350 return isBlockListPagedOut(deadline
, m_oldGen
.toSpace
)
351 || isBlockListPagedOut(deadline
, m_oldGen
.fromSpace
)
352 || isBlockListPagedOut(deadline
, &m_oldGen
.oversizeBlocks
)
353 || isBlockListPagedOut(deadline
, m_newGen
.toSpace
)
354 || isBlockListPagedOut(deadline
, m_newGen
.fromSpace
)
355 || isBlockListPagedOut(deadline
, &m_newGen
.oversizeBlocks
);