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 "Operations.h"
36 CopiedSpace::CopiedSpace(Heap
* heap
)
40 , m_inCopyingPhase(false)
41 , m_shouldDoCopyPhase(false)
42 , m_numberOfLoanedBlocks(0)
47 CopiedSpace::~CopiedSpace()
49 while (!m_toSpace
->isEmpty())
50 m_heap
->blockAllocator().deallocate(CopiedBlock::destroy(m_toSpace
->removeHead()));
52 while (!m_fromSpace
->isEmpty())
53 m_heap
->blockAllocator().deallocate(CopiedBlock::destroy(m_fromSpace
->removeHead()));
55 while (!m_oversizeBlocks
.isEmpty())
56 m_heap
->blockAllocator().deallocateCustomSize(CopiedBlock::destroy(m_oversizeBlocks
.removeHead()));
59 void CopiedSpace::init()
61 m_toSpace
= &m_blocks1
;
62 m_fromSpace
= &m_blocks2
;
67 CheckedBoolean
CopiedSpace::tryAllocateSlowCase(size_t bytes
, void** outPtr
)
69 if (isOversize(bytes
))
70 return tryAllocateOversize(bytes
, outPtr
);
72 ASSERT(m_heap
->vm()->apiLock().currentThreadIsHoldingLock());
73 m_heap
->didAllocate(m_allocator
.currentCapacity());
77 *outPtr
= m_allocator
.forceAllocate(bytes
);
81 CheckedBoolean
CopiedSpace::tryAllocateOversize(size_t bytes
, void** outPtr
)
83 ASSERT(isOversize(bytes
));
85 CopiedBlock
* block
= CopiedBlock::create(m_heap
->blockAllocator().allocateCustomSize(sizeof(CopiedBlock
) + bytes
, CopiedBlock::blockSize
));
86 m_oversizeBlocks
.push(block
);
87 m_blockFilter
.add(reinterpret_cast<Bits
>(block
));
88 m_blockSet
.add(block
);
90 CopiedAllocator allocator
;
91 allocator
.setCurrentBlock(block
);
92 *outPtr
= allocator
.forceAllocate(bytes
);
93 allocator
.resetCurrentBlock();
95 m_heap
->didAllocate(block
->region()->blockSize());
100 CheckedBoolean
CopiedSpace::tryReallocate(void** ptr
, size_t oldSize
, size_t newSize
)
102 if (oldSize
>= newSize
)
106 ASSERT(!m_heap
->vm()->isInitializingObject());
108 if (CopiedSpace::blockFor(oldPtr
)->isOversize() || isOversize(newSize
))
109 return tryReallocateOversize(ptr
, oldSize
, newSize
);
111 if (m_allocator
.tryReallocate(oldPtr
, oldSize
, newSize
))
115 if (!tryAllocate(newSize
, &result
)) {
119 memcpy(result
, oldPtr
, oldSize
);
124 CheckedBoolean
CopiedSpace::tryReallocateOversize(void** ptr
, size_t oldSize
, size_t newSize
)
126 ASSERT(isOversize(oldSize
) || isOversize(newSize
));
127 ASSERT(newSize
> oldSize
);
132 if (!tryAllocateOversize(newSize
, &newPtr
)) {
137 memcpy(newPtr
, oldPtr
, oldSize
);
139 CopiedBlock
* oldBlock
= CopiedSpace::blockFor(oldPtr
);
140 if (oldBlock
->isOversize()) {
141 m_oversizeBlocks
.remove(oldBlock
);
142 m_blockSet
.remove(oldBlock
);
143 m_heap
->blockAllocator().deallocateCustomSize(CopiedBlock::destroy(oldBlock
));
150 void CopiedSpace::doneFillingBlock(CopiedBlock
* block
, CopiedBlock
** exchange
)
152 ASSERT(m_inCopyingPhase
);
155 *exchange
= allocateBlockForCopyingPhase();
160 if (!block
->dataSize()) {
161 recycleBorrowedBlock(block
);
165 block
->zeroFillWilderness();
168 SpinLockHolder
locker(&m_toSpaceLock
);
169 m_toSpace
->push(block
);
170 m_blockSet
.add(block
);
171 m_blockFilter
.add(reinterpret_cast<Bits
>(block
));
175 MutexLocker
locker(m_loanedBlocksLock
);
176 ASSERT(m_numberOfLoanedBlocks
> 0);
177 ASSERT(m_inCopyingPhase
);
178 m_numberOfLoanedBlocks
--;
179 if (!m_numberOfLoanedBlocks
)
180 m_loanedBlocksCondition
.signal();
184 void CopiedSpace::startedCopying()
186 std::swap(m_fromSpace
, m_toSpace
);
188 m_blockFilter
.reset();
189 m_allocator
.resetCurrentBlock();
191 CopiedBlock
* next
= 0;
192 size_t totalLiveBytes
= 0;
193 size_t totalUsableBytes
= 0;
194 for (CopiedBlock
* block
= m_fromSpace
->head(); block
; block
= next
) {
195 next
= block
->next();
196 if (!block
->isPinned() && block
->canBeRecycled()) {
197 recycleEvacuatedBlock(block
);
200 totalLiveBytes
+= block
->liveBytes();
201 totalUsableBytes
+= block
->payloadCapacity();
204 CopiedBlock
* block
= m_oversizeBlocks
.head();
206 CopiedBlock
* next
= block
->next();
207 if (block
->isPinned()) {
208 m_blockFilter
.add(reinterpret_cast<Bits
>(block
));
209 totalLiveBytes
+= block
->payloadCapacity();
210 totalUsableBytes
+= block
->payloadCapacity();
211 block
->didSurviveGC();
213 m_oversizeBlocks
.remove(block
);
214 m_blockSet
.remove(block
);
215 m_heap
->blockAllocator().deallocateCustomSize(CopiedBlock::destroy(block
));
220 double markedSpaceBytes
= m_heap
->objectSpace().capacity();
221 double totalFragmentation
= ((double)totalLiveBytes
+ markedSpaceBytes
) / ((double)totalUsableBytes
+ markedSpaceBytes
);
222 m_shouldDoCopyPhase
= totalFragmentation
<= Options::minHeapUtilization();
223 if (!m_shouldDoCopyPhase
)
226 ASSERT(m_shouldDoCopyPhase
);
227 ASSERT(!m_inCopyingPhase
);
228 ASSERT(!m_numberOfLoanedBlocks
);
229 m_inCopyingPhase
= true;
232 void CopiedSpace::doneCopying()
235 MutexLocker
locker(m_loanedBlocksLock
);
236 while (m_numberOfLoanedBlocks
> 0)
237 m_loanedBlocksCondition
.wait(m_loanedBlocksLock
);
240 ASSERT(m_inCopyingPhase
== m_shouldDoCopyPhase
);
241 m_inCopyingPhase
= false;
243 while (!m_fromSpace
->isEmpty()) {
244 CopiedBlock
* block
= m_fromSpace
->removeHead();
245 // All non-pinned blocks in from-space should have been reclaimed as they were evacuated.
246 ASSERT(block
->isPinned() || !m_shouldDoCopyPhase
);
247 block
->didSurviveGC();
248 // We don't add the block to the blockSet because it was never removed.
249 ASSERT(m_blockSet
.contains(block
));
250 m_blockFilter
.add(reinterpret_cast<Bits
>(block
));
251 m_toSpace
->push(block
);
254 if (!m_toSpace
->head())
257 m_allocator
.setCurrentBlock(m_toSpace
->head());
259 m_shouldDoCopyPhase
= false;
262 size_t CopiedSpace::size()
264 size_t calculatedSize
= 0;
266 for (CopiedBlock
* block
= m_toSpace
->head(); block
; block
= block
->next())
267 calculatedSize
+= block
->size();
269 for (CopiedBlock
* block
= m_fromSpace
->head(); block
; block
= block
->next())
270 calculatedSize
+= block
->size();
272 for (CopiedBlock
* block
= m_oversizeBlocks
.head(); block
; block
= block
->next())
273 calculatedSize
+= block
->size();
275 return calculatedSize
;
278 size_t CopiedSpace::capacity()
280 size_t calculatedCapacity
= 0;
282 for (CopiedBlock
* block
= m_toSpace
->head(); block
; block
= block
->next())
283 calculatedCapacity
+= block
->capacity();
285 for (CopiedBlock
* block
= m_fromSpace
->head(); block
; block
= block
->next())
286 calculatedCapacity
+= block
->capacity();
288 for (CopiedBlock
* block
= m_oversizeBlocks
.head(); block
; block
= block
->next())
289 calculatedCapacity
+= block
->capacity();
291 return calculatedCapacity
;
294 static bool isBlockListPagedOut(double deadline
, DoublyLinkedList
<CopiedBlock
>* list
)
296 unsigned itersSinceLastTimeCheck
= 0;
297 CopiedBlock
* current
= list
->head();
299 current
= current
->next();
300 ++itersSinceLastTimeCheck
;
301 if (itersSinceLastTimeCheck
>= Heap::s_timeCheckResolution
) {
302 double currentTime
= WTF::monotonicallyIncreasingTime();
303 if (currentTime
> deadline
)
305 itersSinceLastTimeCheck
= 0;
312 bool CopiedSpace::isPagedOut(double deadline
)
314 return isBlockListPagedOut(deadline
, m_toSpace
)
315 || isBlockListPagedOut(deadline
, m_fromSpace
)
316 || isBlockListPagedOut(deadline
, &m_oversizeBlocks
);