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