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