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)
45 CopiedSpace::~CopiedSpace()
47 while (!m_oldGen
.toSpace
->isEmpty())
48 m_heap
->blockAllocator().deallocate(CopiedBlock::destroy(m_oldGen
.toSpace
->removeHead()));
50 while (!m_oldGen
.fromSpace
->isEmpty())
51 m_heap
->blockAllocator().deallocate(CopiedBlock::destroy(m_oldGen
.fromSpace
->removeHead()));
53 while (!m_oldGen
.oversizeBlocks
.isEmpty())
54 m_heap
->blockAllocator().deallocateCustomSize(CopiedBlock::destroy(m_oldGen
.oversizeBlocks
.removeHead()));
56 while (!m_newGen
.toSpace
->isEmpty())
57 m_heap
->blockAllocator().deallocate(CopiedBlock::destroy(m_newGen
.toSpace
->removeHead()));
59 while (!m_newGen
.fromSpace
->isEmpty())
60 m_heap
->blockAllocator().deallocate(CopiedBlock::destroy(m_newGen
.fromSpace
->removeHead()));
62 while (!m_newGen
.oversizeBlocks
.isEmpty())
63 m_heap
->blockAllocator().deallocateCustomSize(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(m_heap
->blockAllocator().allocateCustomSize(sizeof(CopiedBlock
) + bytes
, CopiedBlock::blockSize
));
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
->region()->blockSize());
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 if (oldBlock
->isOld())
160 m_oldGen
.oversizeBlocks
.remove(oldBlock
);
162 m_newGen
.oversizeBlocks
.remove(oldBlock
);
163 m_blockSet
.remove(oldBlock
);
164 m_heap
->blockAllocator().deallocateCustomSize(CopiedBlock::destroy(oldBlock
));
171 void CopiedSpace::doneFillingBlock(CopiedBlock
* block
, CopiedBlock
** exchange
)
173 ASSERT(m_inCopyingPhase
);
176 *exchange
= allocateBlockForCopyingPhase();
181 if (!block
->dataSize()) {
182 recycleBorrowedBlock(block
);
186 block
->zeroFillWilderness();
189 // Always put the block into the old gen because it's being promoted!
190 SpinLockHolder
locker(&m_toSpaceLock
);
191 m_oldGen
.toSpace
->push(block
);
192 m_blockSet
.add(block
);
193 m_oldGen
.blockFilter
.add(reinterpret_cast<Bits
>(block
));
197 MutexLocker
locker(m_loanedBlocksLock
);
198 ASSERT(m_numberOfLoanedBlocks
> 0);
199 ASSERT(m_inCopyingPhase
);
200 m_numberOfLoanedBlocks
--;
201 if (!m_numberOfLoanedBlocks
)
202 m_loanedBlocksCondition
.signal();
206 void CopiedSpace::didStartFullCollection()
208 ASSERT(heap()->operationInProgress() == FullCollection
);
209 ASSERT(m_oldGen
.fromSpace
->isEmpty());
210 ASSERT(m_newGen
.fromSpace
->isEmpty());
213 for (CopiedBlock
* block
= m_newGen
.toSpace
->head(); block
; block
= block
->next())
214 ASSERT(!block
->liveBytes());
216 for (CopiedBlock
* block
= m_newGen
.oversizeBlocks
.head(); block
; block
= block
->next())
217 ASSERT(!block
->liveBytes());
220 for (CopiedBlock
* block
= m_oldGen
.toSpace
->head(); block
; block
= block
->next())
221 block
->didSurviveGC();
223 for (CopiedBlock
* block
= m_oldGen
.oversizeBlocks
.head(); block
; block
= block
->next())
224 block
->didSurviveGC();
227 void CopiedSpace::doneCopying()
230 MutexLocker
locker(m_loanedBlocksLock
);
231 while (m_numberOfLoanedBlocks
> 0)
232 m_loanedBlocksCondition
.wait(m_loanedBlocksLock
);
235 ASSERT(m_inCopyingPhase
== m_shouldDoCopyPhase
);
236 m_inCopyingPhase
= false;
238 DoublyLinkedList
<CopiedBlock
>* toSpace
;
239 DoublyLinkedList
<CopiedBlock
>* fromSpace
;
240 TinyBloomFilter
* blockFilter
;
241 if (heap()->operationInProgress() == FullCollection
) {
242 toSpace
= m_oldGen
.toSpace
;
243 fromSpace
= m_oldGen
.fromSpace
;
244 blockFilter
= &m_oldGen
.blockFilter
;
246 toSpace
= m_newGen
.toSpace
;
247 fromSpace
= m_newGen
.fromSpace
;
248 blockFilter
= &m_newGen
.blockFilter
;
251 while (!fromSpace
->isEmpty()) {
252 CopiedBlock
* block
= fromSpace
->removeHead();
253 // We don't add the block to the blockSet because it was never removed.
254 ASSERT(m_blockSet
.contains(block
));
255 blockFilter
->add(reinterpret_cast<Bits
>(block
));
256 block
->didSurviveGC();
257 toSpace
->push(block
);
260 if (heap()->operationInProgress() == EdenCollection
) {
261 m_oldGen
.toSpace
->append(*m_newGen
.toSpace
);
262 m_oldGen
.oversizeBlocks
.append(m_newGen
.oversizeBlocks
);
263 m_oldGen
.blockFilter
.add(m_newGen
.blockFilter
);
264 m_newGen
.blockFilter
.reset();
267 ASSERT(m_newGen
.toSpace
->isEmpty());
268 ASSERT(m_newGen
.fromSpace
->isEmpty());
269 ASSERT(m_newGen
.oversizeBlocks
.isEmpty());
273 m_shouldDoCopyPhase
= false;
276 size_t CopiedSpace::size()
278 size_t calculatedSize
= 0;
280 for (CopiedBlock
* block
= m_oldGen
.toSpace
->head(); block
; block
= block
->next())
281 calculatedSize
+= block
->size();
283 for (CopiedBlock
* block
= m_oldGen
.fromSpace
->head(); block
; block
= block
->next())
284 calculatedSize
+= block
->size();
286 for (CopiedBlock
* block
= m_oldGen
.oversizeBlocks
.head(); block
; block
= block
->next())
287 calculatedSize
+= block
->size();
289 for (CopiedBlock
* block
= m_newGen
.toSpace
->head(); block
; block
= block
->next())
290 calculatedSize
+= block
->size();
292 for (CopiedBlock
* block
= m_newGen
.fromSpace
->head(); block
; block
= block
->next())
293 calculatedSize
+= block
->size();
295 for (CopiedBlock
* block
= m_newGen
.oversizeBlocks
.head(); block
; block
= block
->next())
296 calculatedSize
+= block
->size();
298 return calculatedSize
;
301 size_t CopiedSpace::capacity()
303 size_t calculatedCapacity
= 0;
305 for (CopiedBlock
* block
= m_oldGen
.toSpace
->head(); block
; block
= block
->next())
306 calculatedCapacity
+= block
->capacity();
308 for (CopiedBlock
* block
= m_oldGen
.fromSpace
->head(); block
; block
= block
->next())
309 calculatedCapacity
+= block
->capacity();
311 for (CopiedBlock
* block
= m_oldGen
.oversizeBlocks
.head(); block
; block
= block
->next())
312 calculatedCapacity
+= block
->capacity();
314 for (CopiedBlock
* block
= m_newGen
.toSpace
->head(); block
; block
= block
->next())
315 calculatedCapacity
+= block
->capacity();
317 for (CopiedBlock
* block
= m_newGen
.fromSpace
->head(); block
; block
= block
->next())
318 calculatedCapacity
+= block
->capacity();
320 for (CopiedBlock
* block
= m_newGen
.oversizeBlocks
.head(); block
; block
= block
->next())
321 calculatedCapacity
+= block
->capacity();
323 return calculatedCapacity
;
326 static bool isBlockListPagedOut(double deadline
, DoublyLinkedList
<CopiedBlock
>* list
)
328 unsigned itersSinceLastTimeCheck
= 0;
329 CopiedBlock
* current
= list
->head();
331 current
= current
->next();
332 ++itersSinceLastTimeCheck
;
333 if (itersSinceLastTimeCheck
>= Heap::s_timeCheckResolution
) {
334 double currentTime
= WTF::monotonicallyIncreasingTime();
335 if (currentTime
> deadline
)
337 itersSinceLastTimeCheck
= 0;
344 bool CopiedSpace::isPagedOut(double deadline
)
346 return isBlockListPagedOut(deadline
, m_oldGen
.toSpace
)
347 || isBlockListPagedOut(deadline
, m_oldGen
.fromSpace
)
348 || isBlockListPagedOut(deadline
, &m_oldGen
.oversizeBlocks
)
349 || isBlockListPagedOut(deadline
, m_newGen
.toSpace
)
350 || isBlockListPagedOut(deadline
, m_newGen
.fromSpace
)
351 || isBlockListPagedOut(deadline
, &m_newGen
.oversizeBlocks
);