89cc6f82e149e9892c3b49c4367f81367ed6e9ba
[WebKit.git] / Source / JavaScriptCore / runtime / Heap.cpp
1 /*
2  *  Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009 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 "CollectorHeapIterator.h"
26 #include "ConservativeSet.h"
27 #include "GCActivityCallback.h"
28 #include "GCHandle.h"
29 #include "Interpreter.h"
30 #include "JSGlobalData.h"
31 #include "JSGlobalObject.h"
32 #include "JSLock.h"
33 #include "JSONObject.h"
34 #include "Tracing.h"
35
36 #define COLLECT_ON_EVERY_ALLOCATION 0
37
38 namespace JSC {
39
40 Heap::Heap(JSGlobalData* globalData)
41     : m_markedSpace(globalData)
42     , m_operationInProgress(NoOperation)
43     , m_markListSet(0)
44     , m_activityCallback(DefaultGCActivityCallback::create(this))
45     , m_globalData(globalData)
46     , m_machineStackMarker(this)
47     , m_markStack(globalData->jsArrayVPtr)
48     , m_extraCost(0)
49 {
50     (*m_activityCallback)();
51 }
52
53 Heap::~Heap()
54 {
55     // The destroy function must already have been called, so assert this.
56     ASSERT(!m_globalData);
57 }
58
59 void Heap::destroy()
60 {
61     JSLock lock(SilenceAssertionsOnly);
62
63     if (!m_globalData)
64         return;
65
66     ASSERT(!m_globalData->dynamicGlobalObject);
67     ASSERT(m_operationInProgress == NoOperation);
68     
69     // The global object is not GC protected at this point, so sweeping may delete it
70     // (and thus the global data) before other objects that may use the global data.
71     RefPtr<JSGlobalData> protect(m_globalData);
72
73     delete m_markListSet;
74     m_markListSet = 0;
75
76     m_markedSpace.destroy();
77
78     m_globalData = 0;
79 }
80
81 void Heap::reportExtraMemoryCostSlowCase(size_t cost)
82 {
83     // Our frequency of garbage collection tries to balance memory use against speed
84     // by collecting based on the number of newly created values. However, for values
85     // that hold on to a great deal of memory that's not in the form of other JS values,
86     // that is not good enough - in some cases a lot of those objects can pile up and
87     // use crazy amounts of memory without a GC happening. So we track these extra
88     // memory costs. Only unusually large objects are noted, and we only keep track
89     // of this extra cost until the next GC. In garbage collected languages, most values
90     // are either very short lived temporaries, or have extremely long lifetimes. So
91     // if a large value survives one garbage collection, there is not much point to
92     // collecting more frequently as long as it stays alive.
93
94     if (m_extraCost > maxExtraCost && m_extraCost > m_markedSpace.capacity() / 2)
95         collectAllGarbage();
96     m_extraCost += cost;
97 }
98
99 void* Heap::allocate(size_t s)
100 {
101     ASSERT(globalData()->identifierTable == wtfThreadData().currentIdentifierTable());
102     ASSERT(JSLock::lockCount() > 0);
103     ASSERT(JSLock::currentThreadIsHoldingLock());
104     ASSERT_UNUSED(s, s <= HeapConstants::cellSize);
105     ASSERT(m_operationInProgress == NoOperation);
106
107 #if COLLECT_ON_EVERY_ALLOCATION
108     collectAllGarbage();
109     ASSERT(m_operationInProgress == NoOperation);
110 #endif
111
112     m_operationInProgress = Allocation;
113     void* result = m_markedSpace.allocate(s);
114     m_operationInProgress = NoOperation;
115     if (!result) {
116         reset(DoNotSweep);
117
118         m_operationInProgress = Allocation;
119         result = m_markedSpace.allocate(s);
120         m_operationInProgress = NoOperation;
121     }
122
123     ASSERT(result);
124     return result;
125 }
126
127 void Heap::updateWeakGCHandles()
128 {
129     for (unsigned i = 0; i < m_weakGCHandlePools.size(); ++i)
130         weakGCHandlePool(i)->update();
131 }
132
133 void WeakGCHandlePool::update()
134 {
135     for (unsigned i = 1; i < WeakGCHandlePool::numPoolEntries; ++i) {
136         if (m_entries[i].isValidPtr()) {
137             JSCell* cell = m_entries[i].get();
138             if (!cell || !Heap::isMarked(cell))
139                 m_entries[i].invalidate();
140         }
141     }
142 }
143
144 WeakGCHandle* Heap::addWeakGCHandle(JSCell* ptr)
145 {
146     for (unsigned i = 0; i < m_weakGCHandlePools.size(); ++i)
147         if (!weakGCHandlePool(i)->isFull())
148             return weakGCHandlePool(i)->allocate(ptr);
149
150     PageAllocationAligned allocation = PageAllocationAligned::allocate(WeakGCHandlePool::poolSize, WeakGCHandlePool::poolSize, OSAllocator::JSGCHeapPages);
151     m_weakGCHandlePools.append(allocation);
152
153     WeakGCHandlePool* pool = new (allocation.base()) WeakGCHandlePool();
154     return pool->allocate(ptr);
155 }
156
157 void Heap::protect(JSValue k)
158 {
159     ASSERT(k);
160     ASSERT(JSLock::currentThreadIsHoldingLock() || !m_globalData->isSharedInstance());
161
162     if (!k.isCell())
163         return;
164
165     m_protectedValues.add(k.asCell());
166 }
167
168 bool Heap::unprotect(JSValue k)
169 {
170     ASSERT(k);
171     ASSERT(JSLock::currentThreadIsHoldingLock() || !m_globalData->isSharedInstance());
172
173     if (!k.isCell())
174         return false;
175
176     return m_protectedValues.remove(k.asCell());
177 }
178
179 void Heap::markProtectedObjects(MarkStack& markStack)
180 {
181     ProtectCountSet::iterator end = m_protectedValues.end();
182     for (ProtectCountSet::iterator it = m_protectedValues.begin(); it != end; ++it)
183         markStack.deprecatedAppend(&it->first);
184 }
185
186 void Heap::pushTempSortVector(Vector<ValueStringPair>* tempVector)
187 {
188     m_tempSortingVectors.append(tempVector);
189 }
190
191 void Heap::popTempSortVector(Vector<ValueStringPair>* tempVector)
192 {
193     ASSERT_UNUSED(tempVector, tempVector == m_tempSortingVectors.last());
194     m_tempSortingVectors.removeLast();
195 }
196     
197 void Heap::markTempSortVectors(MarkStack& markStack)
198 {
199     typedef Vector<Vector<ValueStringPair>* > VectorOfValueStringVectors;
200
201     VectorOfValueStringVectors::iterator end = m_tempSortingVectors.end();
202     for (VectorOfValueStringVectors::iterator it = m_tempSortingVectors.begin(); it != end; ++it) {
203         Vector<ValueStringPair>* tempSortingVector = *it;
204
205         Vector<ValueStringPair>::iterator vectorEnd = tempSortingVector->end();
206         for (Vector<ValueStringPair>::iterator vectorIt = tempSortingVector->begin(); vectorIt != vectorEnd; ++vectorIt) {
207             if (vectorIt->first)
208                 markStack.deprecatedAppend(&vectorIt->first);
209         }
210     }
211 }
212
213 inline RegisterFile& Heap::registerFile()
214 {
215     return m_globalData->interpreter->registerFile();
216 }
217
218 void Heap::markRoots()
219 {
220 #ifndef NDEBUG
221     if (m_globalData->isSharedInstance()) {
222         ASSERT(JSLock::lockCount() > 0);
223         ASSERT(JSLock::currentThreadIsHoldingLock());
224     }
225 #endif
226
227     ASSERT(m_operationInProgress == NoOperation);
228     if (m_operationInProgress != NoOperation)
229         CRASH();
230
231     m_operationInProgress = Collection;
232
233     // We gather the conservative set before clearing mark bits, because
234     // conservative gathering uses the mark bits from our last mark pass to
235     // determine whether a reference is valid.
236     ConservativeSet conservativeSet(this);
237     m_machineStackMarker.markMachineStackConservatively(conservativeSet);
238     conservativeSet.add(registerFile().start(), registerFile().end());
239
240     // Reset mark bits.
241     m_markedSpace.clearMarkBits();
242
243     MarkStack& markStack = m_markStack;
244     conservativeSet.mark(markStack);
245     markStack.drain();
246
247     // Mark explicitly registered roots.
248     markProtectedObjects(markStack);
249     markStack.drain();
250     
251     // Mark temporary vector for Array sorting
252     markTempSortVectors(markStack);
253     markStack.drain();
254     
255     HashSet<GlobalCodeBlock*>::const_iterator end = m_codeBlocks.end();
256     for (HashSet<GlobalCodeBlock*>::const_iterator it = m_codeBlocks.begin(); it != end; ++it)
257         (*it)->markAggregate(markStack);
258     markStack.drain();
259
260     // Mark misc. other roots.
261     if (m_markListSet && m_markListSet->size())
262         MarkedArgumentBuffer::markLists(markStack, *m_markListSet);
263     if (m_globalData->exception)
264         markStack.append(&m_globalData->exception);
265     if (m_globalData->firstStringifierToMark)
266         JSONObject::markStringifiers(markStack, m_globalData->firstStringifierToMark);
267     markStack.drain();
268
269     // Mark the small strings cache last, since it will clear itself if nothing
270     // else has marked it.
271     m_globalData->smallStrings.markChildren(markStack);
272
273     markStack.drain();
274     markStack.compact();
275
276     updateWeakGCHandles();
277
278     m_operationInProgress = NoOperation;
279 }
280
281 size_t Heap::objectCount() const
282 {
283     return m_markedSpace.objectCount();
284 }
285
286 size_t Heap::size() const
287 {
288     return m_markedSpace.size();
289 }
290
291 size_t Heap::capacity() const
292 {
293     return m_markedSpace.capacity();
294 }
295
296 size_t Heap::globalObjectCount()
297 {
298     return m_globalData->globalObjects.uncheckedSize();
299 }
300
301 size_t Heap::protectedGlobalObjectCount()
302 {
303     size_t count = 0;
304
305     GlobalObjectMap& map = m_globalData->globalObjects;
306     GlobalObjectMap::iterator end = map.uncheckedEnd();
307     for (GlobalObjectMap::iterator it = map.uncheckedBegin(); it != end; ++it) {
308         if (map.isValid(it) && m_protectedValues.contains(it->second.get()))
309             ++count;
310     }
311
312     return count;
313 }
314
315 size_t Heap::protectedObjectCount()
316 {
317     return m_protectedValues.size();
318 }
319
320 static const char* typeName(JSCell* cell)
321 {
322     if (cell->isString())
323         return "string";
324     if (cell->isGetterSetter())
325         return "Getter-Setter";
326     if (cell->isAPIValueWrapper())
327         return "API wrapper";
328     if (cell->isPropertyNameIterator())
329         return "For-in iterator";
330     if (!cell->isObject())
331         return "[empty cell]";
332     const ClassInfo* info = cell->classInfo();
333     return info ? info->className : "Object";
334 }
335
336 HashCountedSet<const char*>* Heap::protectedObjectTypeCounts()
337 {
338     HashCountedSet<const char*>* counts = new HashCountedSet<const char*>;
339
340     ProtectCountSet::iterator end = m_protectedValues.end();
341     for (ProtectCountSet::iterator it = m_protectedValues.begin(); it != end; ++it)
342         counts->add(typeName(it->first));
343
344     return counts;
345 }
346
347 HashCountedSet<const char*>* Heap::objectTypeCounts()
348 {
349     HashCountedSet<const char*>* counts = new HashCountedSet<const char*>;
350
351     LiveObjectIterator it = primaryHeapBegin();
352     LiveObjectIterator heapEnd = primaryHeapEnd();
353     for ( ; it != heapEnd; ++it)
354         counts->add(typeName(*it));
355
356     return counts;
357 }
358
359 bool Heap::isBusy()
360 {
361     return m_operationInProgress != NoOperation;
362 }
363
364 void Heap::collectAllGarbage()
365 {
366     reset(DoSweep);
367 }
368
369 void Heap::reset(SweepToggle sweepToggle)
370 {
371     ASSERT(globalData()->identifierTable == wtfThreadData().currentIdentifierTable());
372     JAVASCRIPTCORE_GC_BEGIN();
373
374     markRoots();
375
376     JAVASCRIPTCORE_GC_MARKED();
377
378     m_markedSpace.reset();
379     m_extraCost = 0;
380
381     if (sweepToggle == DoSweep)
382         m_markedSpace.sweep();
383
384     JAVASCRIPTCORE_GC_END();
385
386     (*m_activityCallback)();
387 }
388
389 LiveObjectIterator Heap::primaryHeapBegin()
390 {
391     return m_markedSpace.primaryHeapBegin();
392 }
393
394 LiveObjectIterator Heap::primaryHeapEnd()
395 {
396     return m_markedSpace.primaryHeapEnd();
397 }
398
399 void Heap::setActivityCallback(PassOwnPtr<GCActivityCallback> activityCallback)
400 {
401     m_activityCallback = activityCallback;
402 }
403
404 GCActivityCallback* Heap::activityCallback()
405 {
406     return m_activityCallback.get();
407 }
408
409 } // namespace JSC