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