Unreviewed, rolling out r94445 and r94448.
[WebKit-https.git] / Source / JavaScriptCore / heap / Heap.cpp
1 /*
2  *  Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2011 Apple Inc. All rights reserved.
3  *  Copyright (C) 2007 Eric Seidel <eric@webkit.org>
4  *
5  *  This library is free software; you can redistribute it and/or
6  *  modify it under the terms of the GNU Lesser General Public
7  *  License as published by the Free Software Foundation; either
8  *  version 2 of the License, or (at your option) any later version.
9  *
10  *  This library is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  *  Lesser General Public License for more details.
14  *
15  *  You should have received a copy of the GNU Lesser General Public
16  *  License along with this library; if not, write to the Free Software
17  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
18  *
19  */
20
21 #include "config.h"
22 #include "Heap.h"
23
24 #include "CodeBlock.h"
25 #include "ConservativeRoots.h"
26 #include "GCActivityCallback.h"
27 #include "HeapRootVisitor.h"
28 #include "Interpreter.h"
29 #include "JSGlobalData.h"
30 #include "JSGlobalObject.h"
31 #include "JSLock.h"
32 #include "JSONObject.h"
33 #include "Tracing.h"
34 #include <algorithm>
35
36 #define COLLECT_ON_EVERY_ALLOCATION 0
37
38 using namespace std;
39 using namespace JSC;
40
41 namespace JSC {
42
43 namespace { 
44
45 static const size_t largeHeapSize = 16 * 1024 * 1024;
46 static const size_t smallHeapSize = 512 * 1024;
47
48 static size_t heapSizeForHint(HeapSize heapSize)
49 {
50 #if ENABLE(LARGE_HEAP)
51     if (heapSize == LargeHeap)
52         return largeHeapSize;
53     ASSERT(heapSize == SmallHeap);
54     return smallHeapSize;
55 #else
56     ASSERT_UNUSED(heapSize, heapSize == LargeHeap || heapSize == SmallHeap);
57     return smallHeapSize;
58 #endif
59 }
60
61 static inline bool isValidSharedInstanceThreadState()
62 {
63     if (!JSLock::lockCount())
64         return false;
65
66     if (!JSLock::currentThreadIsHoldingLock())
67         return false;
68
69     return true;
70 }
71
72 static inline bool isValidThreadState(JSGlobalData* globalData)
73 {
74     if (globalData->identifierTable != wtfThreadData().currentIdentifierTable())
75         return false;
76
77     if (globalData->isSharedInstance() && !isValidSharedInstanceThreadState())
78         return false;
79
80     return true;
81 }
82
83 class CountFunctor {
84 public:
85     typedef size_t ReturnType;
86
87     CountFunctor();
88     void count(size_t);
89     ReturnType returnValue();
90
91 private:
92     ReturnType m_count;
93 };
94
95 inline CountFunctor::CountFunctor()
96     : m_count(0)
97 {
98 }
99
100 inline void CountFunctor::count(size_t count)
101 {
102     m_count += count;
103 }
104
105 inline CountFunctor::ReturnType CountFunctor::returnValue()
106 {
107     return m_count;
108 }
109
110 struct ClearMarks : MarkedBlock::VoidFunctor {
111     void operator()(MarkedBlock*);
112 };
113
114 inline void ClearMarks::operator()(MarkedBlock* block)
115 {
116     block->clearMarks();
117     block->notifyMayHaveFreshFreeCells();
118 }
119
120 struct Sweep : MarkedBlock::VoidFunctor {
121     void operator()(MarkedBlock*);
122 };
123
124 inline void Sweep::operator()(MarkedBlock* block)
125 {
126     block->sweep();
127 }
128
129 struct MarkCount : CountFunctor {
130     void operator()(MarkedBlock*);
131 };
132
133 inline void MarkCount::operator()(MarkedBlock* block)
134 {
135     count(block->markCount());
136 }
137
138 struct Size : CountFunctor {
139     void operator()(MarkedBlock*);
140 };
141
142 inline void Size::operator()(MarkedBlock* block)
143 {
144     count(block->markCount() * block->cellSize());
145 }
146
147 struct Capacity : CountFunctor {
148     void operator()(MarkedBlock*);
149 };
150
151 inline void Capacity::operator()(MarkedBlock* block)
152 {
153     count(block->capacity());
154 }
155
156 struct Count : public CountFunctor {
157     void operator()(JSCell*);
158 };
159
160 inline void Count::operator()(JSCell*)
161 {
162     count(1);
163 }
164
165 struct CountIfGlobalObject : CountFunctor {
166     void operator()(JSCell*);
167 };
168
169 inline void CountIfGlobalObject::operator()(JSCell* cell)
170 {
171     if (!cell->isObject())
172         return;
173     if (!asObject(cell)->isGlobalObject())
174         return;
175     count(1);
176 }
177
178 class TakeIfEmpty {
179 public:
180     typedef MarkedBlock* ReturnType;
181
182     TakeIfEmpty(NewSpace*);
183     void operator()(MarkedBlock*);
184     ReturnType returnValue();
185
186 private:
187     NewSpace* m_newSpace;
188     DoublyLinkedList<MarkedBlock> m_empties;
189 };
190
191 inline TakeIfEmpty::TakeIfEmpty(NewSpace* newSpace)
192     : m_newSpace(newSpace)
193 {
194 }
195
196 inline void TakeIfEmpty::operator()(MarkedBlock* block)
197 {
198     if (!block->isEmpty())
199         return;
200
201     m_newSpace->removeBlock(block);
202     m_empties.append(block);
203 }
204
205 inline TakeIfEmpty::ReturnType TakeIfEmpty::returnValue()
206 {
207     return m_empties.head();
208 }
209
210 class RecordType {
211 public:
212     typedef PassOwnPtr<TypeCountSet> ReturnType;
213
214     RecordType();
215     void operator()(JSCell*);
216     ReturnType returnValue();
217
218 private:
219     const char* typeName(JSCell*);
220     OwnPtr<TypeCountSet> m_typeCountSet;
221 };
222
223 inline RecordType::RecordType()
224     : m_typeCountSet(adoptPtr(new TypeCountSet))
225 {
226 }
227
228 inline const char* RecordType::typeName(JSCell* cell)
229 {
230     const ClassInfo* info = cell->classInfo();
231     if (!info || !info->className)
232         return "[unknown]";
233     return info->className;
234 }
235
236 inline void RecordType::operator()(JSCell* cell)
237 {
238     m_typeCountSet->add(typeName(cell));
239 }
240
241 inline PassOwnPtr<TypeCountSet> RecordType::returnValue()
242 {
243     return m_typeCountSet.release();
244 }
245
246 } // anonymous namespace
247
248 Heap::Heap(JSGlobalData* globalData, HeapSize heapSize)
249     : m_heapSize(heapSize)
250     , m_minBytesPerCycle(heapSizeForHint(heapSize))
251     , m_operationInProgress(NoOperation)
252     , m_newSpace(this)
253     , m_extraCost(0)
254     , m_markListSet(0)
255     , m_activityCallback(DefaultGCActivityCallback::create(this))
256     , m_machineThreads(this)
257     , m_slotVisitor(globalData->jsArrayVPtr)
258     , m_handleHeap(globalData)
259     , m_isSafeToCollect(false)
260     , m_globalData(globalData)
261 {
262     m_newSpace.setHighWaterMark(m_minBytesPerCycle);
263     (*m_activityCallback)();
264 #if ENABLE(LAZY_BLOCK_FREEING)
265     m_numberOfFreeBlocks = 0;
266     m_blockFreeingThread = createThread(blockFreeingThreadStartFunc, this, "JavaScriptCore::BlockFree");
267     ASSERT(m_blockFreeingThread);
268 #endif
269 }
270
271 Heap::~Heap()
272 {
273 #if ENABLE(LAZY_BLOCK_FREEING)
274     // destroy our thread
275     {
276         MutexLocker locker(m_freeBlockLock);
277         m_blockFreeingThreadShouldQuit = true;
278         m_freeBlockCondition.broadcast();
279     }
280     waitForThreadCompletion(m_blockFreeingThread, 0);
281 #endif
282     
283     // The destroy function must already have been called, so assert this.
284     ASSERT(!m_globalData);
285 }
286
287 void Heap::destroy()
288 {
289     JSLock lock(SilenceAssertionsOnly);
290
291     if (!m_globalData)
292         return;
293
294     ASSERT(!m_globalData->dynamicGlobalObject);
295     ASSERT(m_operationInProgress == NoOperation);
296     
297     // The global object is not GC protected at this point, so sweeping may delete it
298     // (and thus the global data) before other objects that may use the global data.
299     RefPtr<JSGlobalData> protect(m_globalData);
300
301 #if ENABLE(JIT)
302     m_globalData->jitStubs->clearHostFunctionStubs();
303 #endif
304
305     delete m_markListSet;
306     m_markListSet = 0;
307
308     clearMarks();
309     m_handleHeap.finalizeWeakHandles();
310     m_globalData->smallStrings.finalizeSmallStrings();
311
312     shrink();
313     ASSERT(!size());
314     
315 #if ENABLE(SIMPLE_HEAP_PROFILING)
316     m_slotVisitor.m_visitedTypeCounts.dump(stderr, "Visited Type Counts");
317     m_destroyedTypeCounts.dump(stderr, "Destroyed Type Counts");
318 #endif
319     
320 #if ENABLE(LAZY_BLOCK_FREEING)
321     releaseFreeBlocks();
322 #endif
323
324     m_globalData = 0;
325 }
326
327 #if ENABLE(LAZY_BLOCK_FREEING)
328 void Heap::waitForRelativeTimeWhileHoldingLock(double relative)
329 {
330     if (m_blockFreeingThreadShouldQuit)
331         return;
332     m_freeBlockCondition.timedWait(m_freeBlockLock, currentTime() + relative);
333 }
334
335 void Heap::waitForRelativeTime(double relative)
336 {
337     // If this returns early, that's fine, so long as it doesn't do it too
338     // frequently. It would only be a bug if this function failed to return
339     // when it was asked to do so.
340     
341     MutexLocker locker(m_freeBlockLock);
342     waitForRelativeTimeWhileHoldingLock(relative);
343 }
344
345 void* Heap::blockFreeingThreadStartFunc(void* heap)
346 {
347     static_cast<Heap*>(heap)->blockFreeingThreadMain();
348     return 0;
349 }
350
351 void Heap::blockFreeingThreadMain()
352 {
353     while (!m_blockFreeingThreadShouldQuit) {
354         // Generally wait for one second before scavenging free blocks. This
355         // may return early, particularly when we're being asked to quit.
356         waitForRelativeTime(1.0);
357         if (m_blockFreeingThreadShouldQuit)
358             break;
359         
360         // Now process the list of free blocks. Keep freeing until half of the
361         // blocks that are currently on the list are gone. Assume that a size_t
362         // field can be accessed atomically.
363         size_t currentNumberOfFreeBlocks = m_numberOfFreeBlocks;
364         if (!currentNumberOfFreeBlocks)
365             continue;
366         
367         size_t desiredNumberOfFreeBlocks = currentNumberOfFreeBlocks / 2;
368         
369         while (!m_blockFreeingThreadShouldQuit) {
370             MarkedBlock* block;
371             {
372                 MutexLocker locker(m_freeBlockLock);
373                 if (m_numberOfFreeBlocks <= desiredNumberOfFreeBlocks)
374                     block = 0;
375                 else {
376                     block = m_freeBlocks.removeHead();
377                     ASSERT(block);
378                     m_numberOfFreeBlocks--;
379                 }
380             }
381             
382             if (!block)
383                 break;
384             
385             MarkedBlock::destroy(block);
386         }
387     }
388 }
389 #endif // ENABLE(LAZY_BLOCK_FREEING)
390
391 void Heap::reportExtraMemoryCostSlowCase(size_t cost)
392 {
393     // Our frequency of garbage collection tries to balance memory use against speed
394     // by collecting based on the number of newly created values. However, for values
395     // that hold on to a great deal of memory that's not in the form of other JS values,
396     // that is not good enough - in some cases a lot of those objects can pile up and
397     // use crazy amounts of memory without a GC happening. So we track these extra
398     // memory costs. Only unusually large objects are noted, and we only keep track
399     // of this extra cost until the next GC. In garbage collected languages, most values
400     // are either very short lived temporaries, or have extremely long lifetimes. So
401     // if a large value survives one garbage collection, there is not much point to
402     // collecting more frequently as long as it stays alive.
403
404     if (m_extraCost > maxExtraCost && m_extraCost > m_newSpace.highWaterMark() / 2)
405         collectAllGarbage();
406     m_extraCost += cost;
407 }
408
409 inline void* Heap::tryAllocate(NewSpace::SizeClass& sizeClass)
410 {
411     m_operationInProgress = Allocation;
412     void* result = m_newSpace.allocate(sizeClass);
413     m_operationInProgress = NoOperation;
414     return result;
415 }
416
417 void* Heap::allocateSlowCase(NewSpace::SizeClass& sizeClass)
418 {
419 #if COLLECT_ON_EVERY_ALLOCATION
420     collectAllGarbage();
421     ASSERT(m_operationInProgress == NoOperation);
422 #endif
423
424     void* result = tryAllocate(sizeClass);
425
426     if (LIKELY(result != 0))
427         return result;
428
429     AllocationEffort allocationEffort;
430     
431     if (m_newSpace.waterMark() < m_newSpace.highWaterMark() || !m_isSafeToCollect)
432         allocationEffort = AllocationMustSucceed;
433     else
434         allocationEffort = AllocationCanFail;
435     
436     MarkedBlock* block = allocateBlock(sizeClass.cellSize, allocationEffort);
437     if (block) {
438         m_newSpace.addBlock(sizeClass, block);
439         void* result = tryAllocate(sizeClass);
440         ASSERT(result);
441         return result;
442     }
443
444     collect(DoNotSweep);
445     
446     result = tryAllocate(sizeClass);
447     
448     if (result)
449         return result;
450     
451     ASSERT(m_newSpace.waterMark() < m_newSpace.highWaterMark());
452     
453     m_newSpace.addBlock(sizeClass, allocateBlock(sizeClass.cellSize, AllocationMustSucceed));
454     
455     result = tryAllocate(sizeClass);
456     ASSERT(result);
457     return result;
458 }
459
460 void Heap::protect(JSValue k)
461 {
462     ASSERT(k);
463     ASSERT(JSLock::currentThreadIsHoldingLock() || !m_globalData->isSharedInstance());
464
465     if (!k.isCell())
466         return;
467
468     m_protectedValues.add(k.asCell());
469 }
470
471 bool Heap::unprotect(JSValue k)
472 {
473     ASSERT(k);
474     ASSERT(JSLock::currentThreadIsHoldingLock() || !m_globalData->isSharedInstance());
475
476     if (!k.isCell())
477         return false;
478
479     return m_protectedValues.remove(k.asCell());
480 }
481
482 void Heap::markProtectedObjects(HeapRootVisitor& heapRootVisitor)
483 {
484     ProtectCountSet::iterator end = m_protectedValues.end();
485     for (ProtectCountSet::iterator it = m_protectedValues.begin(); it != end; ++it)
486         heapRootVisitor.visit(&it->first);
487 }
488
489 void Heap::pushTempSortVector(Vector<ValueStringPair>* tempVector)
490 {
491     m_tempSortingVectors.append(tempVector);
492 }
493
494 void Heap::popTempSortVector(Vector<ValueStringPair>* tempVector)
495 {
496     ASSERT_UNUSED(tempVector, tempVector == m_tempSortingVectors.last());
497     m_tempSortingVectors.removeLast();
498 }
499     
500 void Heap::markTempSortVectors(HeapRootVisitor& heapRootVisitor)
501 {
502     typedef Vector<Vector<ValueStringPair>* > VectorOfValueStringVectors;
503
504     VectorOfValueStringVectors::iterator end = m_tempSortingVectors.end();
505     for (VectorOfValueStringVectors::iterator it = m_tempSortingVectors.begin(); it != end; ++it) {
506         Vector<ValueStringPair>* tempSortingVector = *it;
507
508         Vector<ValueStringPair>::iterator vectorEnd = tempSortingVector->end();
509         for (Vector<ValueStringPair>::iterator vectorIt = tempSortingVector->begin(); vectorIt != vectorEnd; ++vectorIt) {
510             if (vectorIt->first)
511                 heapRootVisitor.visit(&vectorIt->first);
512         }
513     }
514 }
515
516 void Heap::harvestWeakReferences()
517 {
518     m_slotVisitor.harvestWeakReferences();
519 }
520
521 inline RegisterFile& Heap::registerFile()
522 {
523     return m_globalData->interpreter->registerFile();
524 }
525
526 void Heap::getConservativeRegisterRoots(HashSet<JSCell*>& roots)
527 {
528     ASSERT(isValidThreadState(m_globalData));
529     if (m_operationInProgress != NoOperation)
530         CRASH();
531     m_operationInProgress = Collection;
532     ConservativeRoots registerFileRoots(&m_blocks);
533     registerFile().gatherConservativeRoots(registerFileRoots);
534     size_t registerFileRootCount = registerFileRoots.size();
535     JSCell** registerRoots = registerFileRoots.roots();
536     for (size_t i = 0; i < registerFileRootCount; i++) {
537         setMarked(registerRoots[i]);
538         roots.add(registerRoots[i]);
539     }
540     m_operationInProgress = NoOperation;
541 }
542
543 void Heap::markRoots()
544 {
545     ASSERT(isValidThreadState(m_globalData));
546     if (m_operationInProgress != NoOperation)
547         CRASH();
548     m_operationInProgress = Collection;
549
550     void* dummy;
551
552     // We gather conservative roots before clearing mark bits because conservative
553     // gathering uses the mark bits to determine whether a reference is valid.
554     ConservativeRoots machineThreadRoots(&m_blocks);
555     m_machineThreads.gatherConservativeRoots(machineThreadRoots, &dummy);
556
557     ConservativeRoots registerFileRoots(&m_blocks);
558     registerFile().gatherConservativeRoots(registerFileRoots);
559
560     clearMarks();
561
562     SlotVisitor& visitor = m_slotVisitor;
563     HeapRootVisitor heapRootVisitor(visitor);
564
565     visitor.append(machineThreadRoots);
566     visitor.drain();
567
568     visitor.append(registerFileRoots);
569     visitor.drain();
570
571     markProtectedObjects(heapRootVisitor);
572     visitor.drain();
573     
574     markTempSortVectors(heapRootVisitor);
575     visitor.drain();
576
577     if (m_markListSet && m_markListSet->size())
578         MarkedArgumentBuffer::markLists(heapRootVisitor, *m_markListSet);
579     if (m_globalData->exception)
580         heapRootVisitor.visit(&m_globalData->exception);
581     visitor.drain();
582
583     m_handleHeap.visitStrongHandles(heapRootVisitor);
584     visitor.drain();
585
586     m_handleStack.visit(heapRootVisitor);
587     visitor.drain();
588
589     harvestWeakReferences();
590
591     // Weak handles must be marked last, because their owners use the set of
592     // opaque roots to determine reachability.
593     int lastOpaqueRootCount;
594     do {
595         lastOpaqueRootCount = visitor.opaqueRootCount();
596         m_handleHeap.visitWeakHandles(heapRootVisitor);
597         visitor.drain();
598     // If the set of opaque roots has grown, more weak handles may have become reachable.
599     } while (lastOpaqueRootCount != visitor.opaqueRootCount());
600
601     visitor.reset();
602
603     m_operationInProgress = NoOperation;
604 }
605
606 void Heap::clearMarks()
607 {
608     forEachBlock<ClearMarks>();
609 }
610
611 void Heap::sweep()
612 {
613     forEachBlock<Sweep>();
614 }
615
616 size_t Heap::objectCount()
617 {
618     return forEachBlock<MarkCount>();
619 }
620
621 size_t Heap::size()
622 {
623     return forEachBlock<Size>();
624 }
625
626 size_t Heap::capacity()
627 {
628     return forEachBlock<Capacity>();
629 }
630
631 size_t Heap::protectedGlobalObjectCount()
632 {
633     return forEachProtectedCell<CountIfGlobalObject>();
634 }
635
636 size_t Heap::globalObjectCount()
637 {
638     return forEachCell<CountIfGlobalObject>();
639 }
640
641 size_t Heap::protectedObjectCount()
642 {
643     return forEachProtectedCell<Count>();
644 }
645
646 PassOwnPtr<TypeCountSet> Heap::protectedObjectTypeCounts()
647 {
648     return forEachProtectedCell<RecordType>();
649 }
650
651 PassOwnPtr<TypeCountSet> Heap::objectTypeCounts()
652 {
653     return forEachCell<RecordType>();
654 }
655
656 void Heap::collectAllGarbage()
657 {
658     if (!m_isSafeToCollect)
659         return;
660     if (!m_globalData->dynamicGlobalObject)
661         m_globalData->recompileAllJSFunctions();
662
663     collect(DoSweep);
664 }
665
666 void Heap::collect(SweepToggle sweepToggle)
667 {
668     ASSERT(globalData()->identifierTable == wtfThreadData().currentIdentifierTable());
669     ASSERT(m_isSafeToCollect);
670     JAVASCRIPTCORE_GC_BEGIN();
671     
672     canonicalizeBlocks();
673     
674     markRoots();
675     m_handleHeap.finalizeWeakHandles();
676     m_globalData->smallStrings.finalizeSmallStrings();
677
678     JAVASCRIPTCORE_GC_MARKED();
679     
680     resetAllocator();
681
682     if (sweepToggle == DoSweep) {
683         sweep();
684         shrink();
685     }
686
687     // To avoid pathological GC churn in large heaps, we set the allocation high
688     // water mark to be proportional to the current size of the heap. The exact
689     // proportion is a bit arbitrary. A 2X multiplier gives a 1:1 (heap size :
690     // new bytes allocated) proportion, and seems to work well in benchmarks.
691     size_t proportionalBytes = 2 * size();
692     m_newSpace.setHighWaterMark(max(proportionalBytes, m_minBytesPerCycle));
693
694     JAVASCRIPTCORE_GC_END();
695
696     (*m_activityCallback)();
697 }
698
699 void Heap::canonicalizeBlocks()
700 {
701     m_newSpace.canonicalizeBlocks();
702 }
703
704 void Heap::resetAllocator()
705 {
706     m_extraCost = 0;
707     m_newSpace.resetAllocator();
708 }
709
710 void Heap::setActivityCallback(PassOwnPtr<GCActivityCallback> activityCallback)
711 {
712     m_activityCallback = activityCallback;
713 }
714
715 GCActivityCallback* Heap::activityCallback()
716 {
717     return m_activityCallback.get();
718 }
719
720 bool Heap::isValidAllocation(size_t bytes)
721 {
722     if (!isValidThreadState(m_globalData))
723         return false;
724
725     if (bytes > NewSpace::maxCellSize)
726         return false;
727
728     if (m_operationInProgress != NoOperation)
729         return false;
730     
731     return true;
732 }
733
734 MarkedBlock* Heap::allocateBlock(size_t cellSize, Heap::AllocationEffort allocationEffort)
735 {
736     MarkedBlock* block;
737     
738 #if !ENABLE(LAZY_BLOCK_FREEING)
739     if (allocationEffort == AllocationCanFail)
740         return 0;
741     
742     block = MarkedBlock::create(this, cellSize);
743 #else
744     {
745         MutexLocker locker(m_freeBlockLock);
746         if (m_numberOfFreeBlocks) {
747             block = m_freeBlocks.removeHead();
748             ASSERT(block);
749             m_numberOfFreeBlocks--;
750         } else
751             block = 0;
752     }
753     if (block)
754         block->initForCellSize(cellSize);
755     else if (allocationEffort == AllocationCanFail)
756         return 0;
757     else
758         block = MarkedBlock::create(this, cellSize);
759 #endif
760     
761     m_blocks.add(block);
762
763     return block;
764 }
765
766 void Heap::freeBlocks(MarkedBlock* head)
767 {
768     MarkedBlock* next;
769     for (MarkedBlock* block = head; block; block = next) {
770         next = block->next();
771
772         m_blocks.remove(block);
773         block->reset();
774 #if !ENABLE(LAZY_BLOCK_FREEING)
775         MarkedBlock::destroy(block);
776 #else
777         MutexLocker locker(m_freeBlockLock);
778         m_freeBlocks.append(block);
779         m_numberOfFreeBlocks++;
780 #endif
781     }
782 }
783
784 void Heap::shrink()
785 {
786     // We record a temporary list of empties to avoid modifying m_blocks while iterating it.
787     TakeIfEmpty takeIfEmpty(&m_newSpace);
788     freeBlocks(forEachBlock(takeIfEmpty));
789 }
790
791 #if ENABLE(LAZY_BLOCK_FREEING)
792 void Heap::releaseFreeBlocks()
793 {
794     while (true) {
795         MarkedBlock* block;
796         {
797             MutexLocker locker(m_freeBlockLock);
798             if (!m_numberOfFreeBlocks)
799                 block = 0;
800             else {
801                 block = m_freeBlocks.removeHead();
802                 ASSERT(block);
803                 m_numberOfFreeBlocks--;
804             }
805         }
806         
807         if (!block)
808             break;
809         
810         MarkedBlock::destroy(block);
811     }
812 }
813 #endif
814
815 #if ENABLE(GGC)
816 void Heap::writeBarrierSlowCase(const JSCell* owner, JSCell* cell)
817 {
818 }
819
820 #else
821
822 void Heap::writeBarrierSlowCase(const JSCell*, JSCell*)
823 {
824 }
825 #endif
826
827 } // namespace JSC