ENH: Add Logging to GC Marking Phase
[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 "CopiedSpace.h"
25 #include "CopiedSpaceInlineMethods.h"
26 #include "CodeBlock.h"
27 #include "ConservativeRoots.h"
28 #include "GCActivityCallback.h"
29 #include "HeapRootVisitor.h"
30 #include "IncrementalSweeper.h"
31 #include "Interpreter.h"
32 #include "JSGlobalData.h"
33 #include "JSGlobalObject.h"
34 #include "JSLock.h"
35 #include "JSONObject.h"
36 #include "Tracing.h"
37 #include "WeakSetInlines.h"
38 #include <algorithm>
39 #include <wtf/RAMSize.h>
40 #include <wtf/CurrentTime.h>
41
42 using namespace std;
43 using namespace JSC;
44
45 namespace JSC {
46
47 namespace { 
48
49 static const size_t largeHeapSize = 32 * MB; // About 1.5X the average webpage.
50 static const size_t smallHeapSize = 1 * MB; // Matches the FastMalloc per-thread cache.
51
52 #if ENABLE(GC_LOGGING)
53 #if COMPILER(CLANG)
54 #define DEFINE_GC_LOGGING_GLOBAL(type, name, arguments) \
55 _Pragma("clang diagnostic push") \
56 _Pragma("clang diagnostic ignored \"-Wglobal-constructors\"") \
57 _Pragma("clang diagnostic ignored \"-Wexit-time-destructors\"") \
58 static type name arguments; \
59 _Pragma("clang diagnostic pop")
60 #else
61 #define DEFINE_GC_LOGGING_GLOBAL(type, name, arguments) \
62 static type name arguments;
63 #endif // COMPILER(CLANG)
64
65 struct GCTimer {
66     GCTimer(const char* name)
67         : m_time(0)
68         , m_min(100000000)
69         , m_max(0)
70         , m_count(0)
71         , m_name(name)
72     {
73     }
74     ~GCTimer()
75     {
76         dataLog("%s: %.2lfms (avg. %.2lf, min. %.2lf, max. %.2lf)\n", m_name, m_time * 1000, m_time * 1000 / m_count, m_min*1000, m_max*1000);
77     }
78     double m_time;
79     double m_min;
80     double m_max;
81     size_t m_count;
82     const char* m_name;
83 };
84
85 struct GCTimerScope {
86     GCTimerScope(GCTimer* timer)
87         : m_timer(timer)
88         , m_start(WTF::currentTime())
89     {
90     }
91     ~GCTimerScope()
92     {
93         double delta = WTF::currentTime() - m_start;
94         if (delta < m_timer->m_min)
95             m_timer->m_min = delta;
96         if (delta > m_timer->m_max)
97             m_timer->m_max = delta;
98         m_timer->m_count++;
99         m_timer->m_time += delta;
100     }
101     GCTimer* m_timer;
102     double m_start;
103 };
104
105 struct GCCounter {
106     GCCounter(const char* name)
107         : m_name(name)
108         , m_count(0)
109         , m_total(0)
110         , m_min(10000000)
111         , m_max(0)
112     {
113     }
114     
115     void count(size_t amount)
116     {
117         m_count++;
118         m_total += amount;
119         if (amount < m_min)
120             m_min = amount;
121         if (amount > m_max)
122             m_max = amount;
123     }
124     ~GCCounter()
125     {
126         dataLog("%s: %zu values (avg. %zu, min. %zu, max. %zu)\n", m_name, m_total, m_total / m_count, m_min, m_max);
127     }
128     const char* m_name;
129     size_t m_count;
130     size_t m_total;
131     size_t m_min;
132     size_t m_max;
133 };
134
135 #define GCPHASE(name) DEFINE_GC_LOGGING_GLOBAL(GCTimer, name##Timer, (#name)); GCTimerScope name##TimerScope(&name##Timer)
136 #define COND_GCPHASE(cond, name1, name2) DEFINE_GC_LOGGING_GLOBAL(GCTimer, name1##Timer, (#name1)); DEFINE_GC_LOGGING_GLOBAL(GCTimer, name2##Timer, (#name2)); GCTimerScope name1##CondTimerScope(cond ? &name1##Timer : &name2##Timer)
137 #define GCCOUNTER(name, value) do { DEFINE_GC_LOGGING_GLOBAL(GCCounter, name##Counter, (#name)); name##Counter.count(value); } while (false)
138     
139 #else
140
141 #define GCPHASE(name) do { } while (false)
142 #define COND_GCPHASE(cond, name1, name2) do { } while (false)
143 #define GCCOUNTER(name, value) do { } while (false)
144 #endif
145
146 static inline size_t minHeapSize(HeapType heapType, size_t ramSize)
147 {
148     if (heapType == LargeHeap)
149         return min(largeHeapSize, ramSize / 4);
150     return smallHeapSize;
151 }
152
153 static inline size_t proportionalHeapSize(size_t heapSize, size_t ramSize)
154 {
155     // Try to stay under 1/2 RAM size to leave room for the DOM, rendering, networking, etc.
156     if (heapSize < ramSize / 4)
157         return 2 * heapSize;
158     if (heapSize < ramSize / 2)
159         return 1.5 * heapSize;
160     return 1.25 * heapSize;
161 }
162
163 static inline bool isValidSharedInstanceThreadState()
164 {
165     if (!JSLock::lockCount())
166         return false;
167
168     if (!JSLock::currentThreadIsHoldingLock())
169         return false;
170
171     return true;
172 }
173
174 static inline bool isValidThreadState(JSGlobalData* globalData)
175 {
176     if (globalData->identifierTable != wtfThreadData().currentIdentifierTable())
177         return false;
178
179     if (globalData->isSharedInstance() && !isValidSharedInstanceThreadState())
180         return false;
181
182     return true;
183 }
184
185 struct Count : public MarkedBlock::CountFunctor {
186     void operator()(JSCell*) { count(1); }
187 };
188
189 struct CountIfGlobalObject : MarkedBlock::CountFunctor {
190     void operator()(JSCell* cell) {
191         if (!cell->isObject())
192             return;
193         if (!asObject(cell)->isGlobalObject())
194             return;
195         count(1);
196     }
197 };
198
199 class RecordType {
200 public:
201     typedef PassOwnPtr<TypeCountSet> ReturnType;
202
203     RecordType();
204     void operator()(JSCell*);
205     ReturnType returnValue();
206
207 private:
208     const char* typeName(JSCell*);
209     OwnPtr<TypeCountSet> m_typeCountSet;
210 };
211
212 inline RecordType::RecordType()
213     : m_typeCountSet(adoptPtr(new TypeCountSet))
214 {
215 }
216
217 inline const char* RecordType::typeName(JSCell* cell)
218 {
219     const ClassInfo* info = cell->classInfo();
220     if (!info || !info->className)
221         return "[unknown]";
222     return info->className;
223 }
224
225 inline void RecordType::operator()(JSCell* cell)
226 {
227     m_typeCountSet->add(typeName(cell));
228 }
229
230 inline PassOwnPtr<TypeCountSet> RecordType::returnValue()
231 {
232     return m_typeCountSet.release();
233 }
234
235 } // anonymous namespace
236
237 Heap::Heap(JSGlobalData* globalData, HeapType heapType)
238     : m_heapType(heapType)
239     , m_ramSize(ramSize())
240     , m_minBytesPerCycle(minHeapSize(m_heapType, m_ramSize))
241     , m_sizeAfterLastCollect(0)
242     , m_bytesAllocatedLimit(m_minBytesPerCycle)
243     , m_bytesAllocated(0)
244     , m_bytesAbandoned(0)
245     , m_operationInProgress(NoOperation)
246     , m_objectSpace(this)
247     , m_storageSpace(this)
248     , m_activityCallback(DefaultGCActivityCallback::create(this))
249     , m_sweeper(IncrementalSweeper::create(this))
250     , m_machineThreads(this)
251     , m_sharedData(globalData)
252     , m_slotVisitor(m_sharedData)
253     , m_handleSet(globalData)
254     , m_isSafeToCollect(false)
255     , m_globalData(globalData)
256     , m_lastGCLength(0)
257     , m_lastCodeDiscardTime(WTF::currentTime())
258 {
259     m_storageSpace.init();
260 }
261
262 Heap::~Heap()
263 {
264 }
265
266 bool Heap::isPagedOut(double deadline)
267 {
268     return m_objectSpace.isPagedOut(deadline) || m_storageSpace.isPagedOut(deadline);
269 }
270
271 // The JSGlobalData is being destroyed and the collector will never run again.
272 // Run all pending finalizers now because we won't get another chance.
273 void Heap::lastChanceToFinalize()
274 {
275     ASSERT(!m_globalData->dynamicGlobalObject);
276     ASSERT(m_operationInProgress == NoOperation);
277
278     // FIXME: Make this a release-mode crash once we're sure no one's doing this.
279     if (size_t size = m_protectedValues.size())
280         WTFLogAlways("ERROR: JavaScriptCore heap deallocated while %ld values were still protected", static_cast<unsigned long>(size));
281
282     m_objectSpace.lastChanceToFinalize();
283
284 #if ENABLE(SIMPLE_HEAP_PROFILING)
285     m_slotVisitor.m_visitedTypeCounts.dump(WTF::dataFile(), "Visited Type Counts");
286     m_destroyedTypeCounts.dump(WTF::dataFile(), "Destroyed Type Counts");
287 #endif
288 }
289
290 void Heap::reportExtraMemoryCostSlowCase(size_t cost)
291 {
292     // Our frequency of garbage collection tries to balance memory use against speed
293     // by collecting based on the number of newly created values. However, for values
294     // that hold on to a great deal of memory that's not in the form of other JS values,
295     // that is not good enough - in some cases a lot of those objects can pile up and
296     // use crazy amounts of memory without a GC happening. So we track these extra
297     // memory costs. Only unusually large objects are noted, and we only keep track
298     // of this extra cost until the next GC. In garbage collected languages, most values
299     // are either very short lived temporaries, or have extremely long lifetimes. So
300     // if a large value survives one garbage collection, there is not much point to
301     // collecting more frequently as long as it stays alive.
302
303     didAllocate(cost);
304     if (shouldCollect())
305         collect(DoNotSweep);
306 }
307
308 void Heap::reportAbandonedObjectGraph()
309 {
310     // Our clients don't know exactly how much memory they
311     // are abandoning so we just guess for them.
312     double abandonedBytes = 0.10 * m_sizeAfterLastCollect;
313
314     // We want to accelerate the next collection. Because memory has just 
315     // been abandoned, the next collection has the potential to 
316     // be more profitable. Since allocation is the trigger for collection, 
317     // we hasten the next collection by pretending that we've allocated more memory. 
318     didAbandon(abandonedBytes);
319 }
320
321 void Heap::didAbandon(size_t bytes)
322 {
323     m_activityCallback->didAllocate(m_bytesAllocated + m_bytesAbandoned);
324     m_bytesAbandoned += bytes;
325 }
326
327 void Heap::protect(JSValue k)
328 {
329     ASSERT(k);
330     ASSERT(JSLock::currentThreadIsHoldingLock() || !m_globalData->isSharedInstance());
331
332     if (!k.isCell())
333         return;
334
335     m_protectedValues.add(k.asCell());
336 }
337
338 bool Heap::unprotect(JSValue k)
339 {
340     ASSERT(k);
341     ASSERT(JSLock::currentThreadIsHoldingLock() || !m_globalData->isSharedInstance());
342
343     if (!k.isCell())
344         return false;
345
346     return m_protectedValues.remove(k.asCell());
347 }
348
349 void Heap::jettisonDFGCodeBlock(PassOwnPtr<CodeBlock> codeBlock)
350 {
351     m_dfgCodeBlocks.jettison(codeBlock);
352 }
353
354 void Heap::markProtectedObjects(HeapRootVisitor& heapRootVisitor)
355 {
356     ProtectCountSet::iterator end = m_protectedValues.end();
357     for (ProtectCountSet::iterator it = m_protectedValues.begin(); it != end; ++it)
358         heapRootVisitor.visit(&it->first);
359 }
360
361 void Heap::pushTempSortVector(Vector<ValueStringPair>* tempVector)
362 {
363     m_tempSortingVectors.append(tempVector);
364 }
365
366 void Heap::popTempSortVector(Vector<ValueStringPair>* tempVector)
367 {
368     ASSERT_UNUSED(tempVector, tempVector == m_tempSortingVectors.last());
369     m_tempSortingVectors.removeLast();
370 }
371
372 void Heap::markTempSortVectors(HeapRootVisitor& heapRootVisitor)
373 {
374     typedef Vector<Vector<ValueStringPair>* > VectorOfValueStringVectors;
375
376     VectorOfValueStringVectors::iterator end = m_tempSortingVectors.end();
377     for (VectorOfValueStringVectors::iterator it = m_tempSortingVectors.begin(); it != end; ++it) {
378         Vector<ValueStringPair>* tempSortingVector = *it;
379
380         Vector<ValueStringPair>::iterator vectorEnd = tempSortingVector->end();
381         for (Vector<ValueStringPair>::iterator vectorIt = tempSortingVector->begin(); vectorIt != vectorEnd; ++vectorIt) {
382             if (vectorIt->first)
383                 heapRootVisitor.visit(&vectorIt->first);
384         }
385     }
386 }
387
388 void Heap::harvestWeakReferences()
389 {
390     m_slotVisitor.harvestWeakReferences();
391 }
392
393 void Heap::finalizeUnconditionalFinalizers()
394 {
395     m_slotVisitor.finalizeUnconditionalFinalizers();
396 }
397
398 inline RegisterFile& Heap::registerFile()
399 {
400     return m_globalData->interpreter->registerFile();
401 }
402
403 void Heap::getConservativeRegisterRoots(HashSet<JSCell*>& roots)
404 {
405     ASSERT(isValidThreadState(m_globalData));
406     ConservativeRoots registerFileRoots(&m_objectSpace.blocks(), &m_storageSpace);
407     registerFile().gatherConservativeRoots(registerFileRoots);
408     size_t registerFileRootCount = registerFileRoots.size();
409     JSCell** registerRoots = registerFileRoots.roots();
410     for (size_t i = 0; i < registerFileRootCount; i++) {
411         setMarked(registerRoots[i]);
412         roots.add(registerRoots[i]);
413     }
414 }
415
416 void Heap::markRoots(bool fullGC)
417 {
418     SamplingRegion samplingRegion("Garbage Collection: Tracing");
419
420     COND_GCPHASE(fullGC, MarkFullRoots, MarkYoungRoots);
421     UNUSED_PARAM(fullGC);
422     ASSERT(isValidThreadState(m_globalData));
423
424 #if ENABLE(OBJECT_MARK_LOGGING)
425     double gcStartTime = WTF::currentTime();
426 #endif
427
428     void* dummy;
429     
430     // We gather conservative roots before clearing mark bits because conservative
431     // gathering uses the mark bits to determine whether a reference is valid.
432     ConservativeRoots machineThreadRoots(&m_objectSpace.blocks(), &m_storageSpace);
433     {
434         GCPHASE(GatherConservativeRoots);
435         m_machineThreads.gatherConservativeRoots(machineThreadRoots, &dummy);
436     }
437
438     ConservativeRoots registerFileRoots(&m_objectSpace.blocks(), &m_storageSpace);
439     m_dfgCodeBlocks.clearMarks();
440     {
441         GCPHASE(GatherRegisterFileRoots);
442         registerFile().gatherConservativeRoots(registerFileRoots, m_dfgCodeBlocks);
443     }
444
445 #if ENABLE(DFG_JIT)
446     ConservativeRoots scratchBufferRoots(&m_objectSpace.blocks(), &m_storageSpace);
447     {
448         GCPHASE(GatherScratchBufferRoots);
449         m_globalData->gatherConservativeRoots(scratchBufferRoots);
450     }
451 #endif
452
453 #if ENABLE(GGC)
454     MarkedBlock::DirtyCellVector dirtyCells;
455     if (!fullGC) {
456         GCPHASE(GatheringDirtyCells);
457         m_objectSpace.gatherDirtyCells(dirtyCells);
458     } else
459 #endif
460     {
461         GCPHASE(clearMarks);
462         m_objectSpace.clearMarks();
463     }
464
465     m_storageSpace.startedCopying();
466     SlotVisitor& visitor = m_slotVisitor;
467     HeapRootVisitor heapRootVisitor(visitor);
468
469     {
470         ParallelModeEnabler enabler(visitor);
471 #if ENABLE(GGC)
472         {
473             size_t dirtyCellCount = dirtyCells.size();
474             GCPHASE(VisitDirtyCells);
475             GCCOUNTER(DirtyCellCount, dirtyCellCount);
476             for (size_t i = 0; i < dirtyCellCount; i++) {
477                 heapRootVisitor.visitChildren(dirtyCells[i]);
478                 visitor.donateAndDrain();
479             }
480         }
481 #endif
482     
483         if (m_globalData->codeBlocksBeingCompiled.size()) {
484             GCPHASE(VisitActiveCodeBlock);
485             for (size_t i = 0; i < m_globalData->codeBlocksBeingCompiled.size(); i++)
486                 m_globalData->codeBlocksBeingCompiled[i]->visitAggregate(visitor);
487         }
488     
489         {
490             GCPHASE(VisitMachineRoots);
491             MARK_LOG_ROOT(visitor, "C++ Stack");
492             visitor.append(machineThreadRoots);
493             visitor.donateAndDrain();
494         }
495         {
496             GCPHASE(VisitRegisterFileRoots);
497             MARK_LOG_ROOT(visitor, "Register File");
498             visitor.append(registerFileRoots);
499             visitor.donateAndDrain();
500         }
501 #if ENABLE(DFG_JIT)
502         {
503             GCPHASE(VisitScratchBufferRoots);
504             MARK_LOG_ROOT(visitor, "Scratch Buffers");
505             visitor.append(scratchBufferRoots);
506             visitor.donateAndDrain();
507         }
508 #endif
509         {
510             GCPHASE(VisitProtectedObjects);
511             MARK_LOG_ROOT(visitor, "Protected Objects");
512             markProtectedObjects(heapRootVisitor);
513             visitor.donateAndDrain();
514         }
515         {
516             GCPHASE(VisitTempSortVectors);
517             MARK_LOG_ROOT(visitor, "Temp Sort Vectors");
518             markTempSortVectors(heapRootVisitor);
519             visitor.donateAndDrain();
520         }
521
522         {
523             GCPHASE(MarkingArgumentBuffers);
524             if (m_markListSet && m_markListSet->size()) {
525                 MARK_LOG_ROOT(visitor, "Argument Buffers");
526                 MarkedArgumentBuffer::markLists(heapRootVisitor, *m_markListSet);
527                 visitor.donateAndDrain();
528             }
529         }
530         if (m_globalData->exception) {
531             GCPHASE(MarkingException);
532             MARK_LOG_ROOT(visitor, "Exceptions");
533             heapRootVisitor.visit(&m_globalData->exception);
534             visitor.donateAndDrain();
535         }
536     
537         {
538             GCPHASE(VisitStrongHandles);
539             MARK_LOG_ROOT(visitor, "Strong Handles");
540             m_handleSet.visitStrongHandles(heapRootVisitor);
541             visitor.donateAndDrain();
542         }
543     
544         {
545             GCPHASE(HandleStack);
546             MARK_LOG_ROOT(visitor, "Handle Stack");
547             m_handleStack.visit(heapRootVisitor);
548             visitor.donateAndDrain();
549         }
550     
551         {
552             GCPHASE(TraceCodeBlocks);
553             MARK_LOG_ROOT(visitor, "Trace Code Blocks");
554             m_dfgCodeBlocks.traceMarkedCodeBlocks(visitor);
555             visitor.donateAndDrain();
556         }
557     
558 #if ENABLE(PARALLEL_GC)
559         {
560             GCPHASE(Convergence);
561             visitor.drainFromShared(SlotVisitor::MasterDrain);
562         }
563 #endif
564     }
565
566     // Weak references must be marked last because their liveness depends on
567     // the liveness of the rest of the object graph.
568     {
569         GCPHASE(VisitingLiveWeakHandles);
570         MARK_LOG_ROOT(visitor, "Live Weak Handles");
571         while (true) {
572             m_objectSpace.visitWeakSets(heapRootVisitor);
573             harvestWeakReferences();
574             if (visitor.isEmpty())
575                 break;
576             {
577                 ParallelModeEnabler enabler(visitor);
578                 visitor.donateAndDrain();
579 #if ENABLE(PARALLEL_GC)
580                 visitor.drainFromShared(SlotVisitor::MasterDrain);
581 #endif
582             }
583         }
584     }
585
586     GCCOUNTER(VisitedValueCount, visitor.visitCount());
587
588     visitor.doneCopying();
589 #if ENABLE(OBJECT_MARK_LOGGING)
590     size_t visitCount = visitor.visitCount();
591 #if ENABLE(PARALLEL_GC)
592     visitCount += m_sharedData.childVisitCount();
593 #endif
594     MARK_LOG_MESSAGE2("\nNumber of live Objects after full GC %lu, took %.6f secs\n", visitCount, WTF::currentTime() - gcStartTime);
595 #endif
596
597     visitor.reset();
598     m_sharedData.reset();
599 #if ENABLE(PARALLEL_GC)
600     m_sharedData.resetChildren();
601 #endif
602     m_storageSpace.doneCopying();
603
604 }
605
606 size_t Heap::objectCount()
607 {
608     return m_objectSpace.objectCount();
609 }
610
611 size_t Heap::size()
612 {
613     return m_objectSpace.size() + m_storageSpace.size();
614 }
615
616 size_t Heap::capacity()
617 {
618     return m_objectSpace.capacity() + m_storageSpace.capacity();
619 }
620
621 size_t Heap::protectedGlobalObjectCount()
622 {
623     return forEachProtectedCell<CountIfGlobalObject>();
624 }
625
626 size_t Heap::globalObjectCount()
627 {
628     return m_objectSpace.forEachCell<CountIfGlobalObject>();
629 }
630
631 size_t Heap::protectedObjectCount()
632 {
633     return forEachProtectedCell<Count>();
634 }
635
636 PassOwnPtr<TypeCountSet> Heap::protectedObjectTypeCounts()
637 {
638     return forEachProtectedCell<RecordType>();
639 }
640
641 PassOwnPtr<TypeCountSet> Heap::objectTypeCounts()
642 {
643     return m_objectSpace.forEachCell<RecordType>();
644 }
645
646 void Heap::discardAllCompiledCode()
647 {
648     // If JavaScript is running, it's not safe to recompile, since we'll end
649     // up throwing away code that is live on the stack.
650     if (m_globalData->dynamicGlobalObject)
651         return;
652
653     for (FunctionExecutable* current = m_functions.head(); current; current = current->next())
654         current->discardCode();
655 }
656
657 void Heap::collectAllGarbage()
658 {
659     if (!m_isSafeToCollect)
660         return;
661
662     collect(DoSweep);
663 }
664
665 static double minute = 60.0;
666
667 void Heap::collect(SweepToggle sweepToggle)
668 {
669     SamplingRegion samplingRegion("Garbage Collection");
670     
671     GCPHASE(Collect);
672     ASSERT(globalData()->identifierTable == wtfThreadData().currentIdentifierTable());
673     ASSERT(m_isSafeToCollect);
674     JAVASCRIPTCORE_GC_BEGIN();
675     if (m_operationInProgress != NoOperation)
676         CRASH();
677     m_operationInProgress = Collection;
678
679     m_activityCallback->willCollect();
680
681     double lastGCStartTime = WTF::currentTime();
682     if (lastGCStartTime - m_lastCodeDiscardTime > minute) {
683         discardAllCompiledCode();
684         m_lastCodeDiscardTime = WTF::currentTime();
685     }
686
687 #if ENABLE(GGC)
688     bool fullGC = sweepToggle == DoSweep;
689     if (!fullGC)
690         fullGC = (capacity() > 4 * m_sizeAfterLastCollect);  
691 #else
692     bool fullGC = true;
693 #endif
694     {
695         GCPHASE(Canonicalize);
696         m_objectSpace.canonicalizeCellLivenessData();
697     }
698
699     markRoots(fullGC);
700     
701     {
702         GCPHASE(ReapingWeakHandles);
703         m_objectSpace.reapWeakSets();
704     }
705
706     {
707         GCPHASE(FinalizeUnconditionalFinalizers);
708         finalizeUnconditionalFinalizers();
709     }
710
711     {
712         GCPHASE(FinalizeWeakHandles);
713         m_objectSpace.sweepWeakSets();
714         m_globalData->smallStrings.finalizeSmallStrings();
715     }
716     
717     JAVASCRIPTCORE_GC_MARKED();
718
719     {
720         GCPHASE(DeleteCodeBlocks);
721         m_dfgCodeBlocks.deleteUnmarkedJettisonedCodeBlocks();
722     }
723
724     if (sweepToggle == DoSweep) {
725         SamplingRegion samplingRegion("Garbage Collection: Sweeping");
726         GCPHASE(Sweeping);
727         m_objectSpace.sweep();
728         m_objectSpace.shrink();
729     }
730
731     m_sweeper->startSweeping(m_objectSpace.blocks().set());
732     m_bytesAbandoned = 0;
733
734     {
735         GCPHASE(ResetAllocators);
736         m_objectSpace.resetAllocators();
737     }
738     
739     size_t currentHeapSize = size();
740     if (fullGC) {
741         m_sizeAfterLastCollect = currentHeapSize;
742
743         // To avoid pathological GC churn in very small and very large heaps, we set
744         // the new allocation limit based on the current size of the heap, with a
745         // fixed minimum.
746         size_t maxHeapSize = max(minHeapSize(m_heapType, m_ramSize), proportionalHeapSize(currentHeapSize, m_ramSize));
747         m_bytesAllocatedLimit = maxHeapSize - currentHeapSize;
748     }
749     m_bytesAllocated = 0;
750     double lastGCEndTime = WTF::currentTime();
751     m_lastGCLength = lastGCEndTime - lastGCStartTime;
752     if (m_operationInProgress != Collection)
753         CRASH();
754     m_operationInProgress = NoOperation;
755     JAVASCRIPTCORE_GC_END();
756 }
757
758 void Heap::setActivityCallback(PassOwnPtr<GCActivityCallback> activityCallback)
759 {
760     m_activityCallback = activityCallback;
761 }
762
763 GCActivityCallback* Heap::activityCallback()
764 {
765     return m_activityCallback.get();
766 }
767
768 IncrementalSweeper* Heap::sweeper()
769 {
770     return m_sweeper.get();
771 }
772
773 void Heap::setGarbageCollectionTimerEnabled(bool enable)
774 {
775     activityCallback()->setEnabled(enable);
776 }
777
778 void Heap::didAllocate(size_t bytes)
779 {
780     m_activityCallback->didAllocate(m_bytesAllocated + m_bytesAbandoned);
781     m_bytesAllocated += bytes;
782 }
783
784 bool Heap::isValidAllocation(size_t bytes)
785 {
786     if (!isValidThreadState(m_globalData))
787         return false;
788
789     if (bytes > MarkedSpace::maxCellSize)
790         return false;
791
792     if (m_operationInProgress != NoOperation)
793         return false;
794     
795     return true;
796 }
797
798 void Heap::addFinalizer(JSCell* cell, Finalizer finalizer)
799 {
800     WeakSet::allocate(cell, &m_finalizerOwner, reinterpret_cast<void*>(finalizer)); // Balanced by FinalizerOwner::finalize().
801 }
802
803 void Heap::FinalizerOwner::finalize(Handle<Unknown> handle, void* context)
804 {
805     HandleSlot slot = handle.slot();
806     Finalizer finalizer = reinterpret_cast<Finalizer>(context);
807     finalizer(slot->asCell());
808     WeakSet::deallocate(WeakImpl::asWeakImpl(slot));
809 }
810
811 void Heap::addFunctionExecutable(FunctionExecutable* executable)
812 {
813     m_functions.append(executable);
814 }
815
816 void Heap::removeFunctionExecutable(FunctionExecutable* executable)
817 {
818     m_functions.remove(executable);
819 }
820
821 } // namespace JSC