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