Home | History | Annotate | Download | only in heap
      1 /*
      2  *  Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009 Apple Inc. All rights reserved.
      3  *  Copyright (C) 2007 Eric Seidel <eric (at) 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 "GCActivityCallback.h"
     27 #include "Interpreter.h"
     28 #include "JSGlobalData.h"
     29 #include "JSGlobalObject.h"
     30 #include "JSLock.h"
     31 #include "JSONObject.h"
     32 #include "Tracing.h"
     33 #include <algorithm>
     34 
     35 #define COLLECT_ON_EVERY_SLOW_ALLOCATION 0
     36 
     37 using namespace std;
     38 
     39 namespace JSC {
     40 
     41 const size_t minBytesPerCycle = 512 * 1024;
     42 
     43 Heap::Heap(JSGlobalData* globalData)
     44     : m_operationInProgress(NoOperation)
     45     , m_markedSpace(globalData)
     46     , m_markListSet(0)
     47     , m_activityCallback(DefaultGCActivityCallback::create(this))
     48     , m_globalData(globalData)
     49     , m_machineThreads(this)
     50     , m_markStack(globalData->jsArrayVPtr)
     51     , m_handleHeap(globalData)
     52     , m_extraCost(0)
     53 {
     54     m_markedSpace.setHighWaterMark(minBytesPerCycle);
     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 #if ENABLE(JIT)
     79     m_globalData->jitStubs->clearHostFunctionStubs();
     80 #endif
     81 
     82     delete m_markListSet;
     83     m_markListSet = 0;
     84     m_markedSpace.clearMarks();
     85     m_handleHeap.finalizeWeakHandles();
     86     m_markedSpace.destroy();
     87 
     88     m_globalData = 0;
     89 }
     90 
     91 void Heap::reportExtraMemoryCostSlowCase(size_t cost)
     92 {
     93     // Our frequency of garbage collection tries to balance memory use against speed
     94     // by collecting based on the number of newly created values. However, for values
     95     // that hold on to a great deal of memory that's not in the form of other JS values,
     96     // that is not good enough - in some cases a lot of those objects can pile up and
     97     // use crazy amounts of memory without a GC happening. So we track these extra
     98     // memory costs. Only unusually large objects are noted, and we only keep track
     99     // of this extra cost until the next GC. In garbage collected languages, most values
    100     // are either very short lived temporaries, or have extremely long lifetimes. So
    101     // if a large value survives one garbage collection, there is not much point to
    102     // collecting more frequently as long as it stays alive.
    103 
    104     if (m_extraCost > maxExtraCost && m_extraCost > m_markedSpace.highWaterMark() / 2)
    105         collectAllGarbage();
    106     m_extraCost += cost;
    107 }
    108 
    109 void* Heap::allocateSlowCase(size_t bytes)
    110 {
    111     ASSERT(globalData()->identifierTable == wtfThreadData().currentIdentifierTable());
    112     ASSERT(JSLock::lockCount() > 0);
    113     ASSERT(JSLock::currentThreadIsHoldingLock());
    114     ASSERT(bytes <= MarkedSpace::maxCellSize);
    115     ASSERT(m_operationInProgress == NoOperation);
    116 
    117 #if COLLECT_ON_EVERY_SLOW_ALLOCATION
    118     collectAllGarbage();
    119     ASSERT(m_operationInProgress == NoOperation);
    120 #endif
    121 
    122     reset(DoNotSweep);
    123 
    124     m_operationInProgress = Allocation;
    125     void* result = m_markedSpace.allocate(bytes);
    126     m_operationInProgress = NoOperation;
    127 
    128     ASSERT(result);
    129     return result;
    130 }
    131 
    132 void Heap::protect(JSValue k)
    133 {
    134     ASSERT(k);
    135     ASSERT(JSLock::currentThreadIsHoldingLock() || !m_globalData->isSharedInstance());
    136 
    137     if (!k.isCell())
    138         return;
    139 
    140     m_protectedValues.add(k.asCell());
    141 }
    142 
    143 bool Heap::unprotect(JSValue k)
    144 {
    145     ASSERT(k);
    146     ASSERT(JSLock::currentThreadIsHoldingLock() || !m_globalData->isSharedInstance());
    147 
    148     if (!k.isCell())
    149         return false;
    150 
    151     return m_protectedValues.remove(k.asCell());
    152 }
    153 
    154 void Heap::markProtectedObjects(HeapRootMarker& heapRootMarker)
    155 {
    156     ProtectCountSet::iterator end = m_protectedValues.end();
    157     for (ProtectCountSet::iterator it = m_protectedValues.begin(); it != end; ++it)
    158         heapRootMarker.mark(&it->first);
    159 }
    160 
    161 void Heap::pushTempSortVector(Vector<ValueStringPair>* tempVector)
    162 {
    163     m_tempSortingVectors.append(tempVector);
    164 }
    165 
    166 void Heap::popTempSortVector(Vector<ValueStringPair>* tempVector)
    167 {
    168     ASSERT_UNUSED(tempVector, tempVector == m_tempSortingVectors.last());
    169     m_tempSortingVectors.removeLast();
    170 }
    171 
    172 void Heap::markTempSortVectors(HeapRootMarker& heapRootMarker)
    173 {
    174     typedef Vector<Vector<ValueStringPair>* > VectorOfValueStringVectors;
    175 
    176     VectorOfValueStringVectors::iterator end = m_tempSortingVectors.end();
    177     for (VectorOfValueStringVectors::iterator it = m_tempSortingVectors.begin(); it != end; ++it) {
    178         Vector<ValueStringPair>* tempSortingVector = *it;
    179 
    180         Vector<ValueStringPair>::iterator vectorEnd = tempSortingVector->end();
    181         for (Vector<ValueStringPair>::iterator vectorIt = tempSortingVector->begin(); vectorIt != vectorEnd; ++vectorIt) {
    182             if (vectorIt->first)
    183                 heapRootMarker.mark(&vectorIt->first);
    184         }
    185     }
    186 }
    187 
    188 inline RegisterFile& Heap::registerFile()
    189 {
    190     return m_globalData->interpreter->registerFile();
    191 }
    192 
    193 void Heap::markRoots()
    194 {
    195 #ifndef NDEBUG
    196     if (m_globalData->isSharedInstance()) {
    197         ASSERT(JSLock::lockCount() > 0);
    198         ASSERT(JSLock::currentThreadIsHoldingLock());
    199     }
    200 #endif
    201 
    202     void* dummy;
    203 
    204     ASSERT(m_operationInProgress == NoOperation);
    205     if (m_operationInProgress != NoOperation)
    206         CRASH();
    207 
    208     m_operationInProgress = Collection;
    209 
    210     MarkStack& markStack = m_markStack;
    211     HeapRootMarker heapRootMarker(markStack);
    212 
    213     // We gather conservative roots before clearing mark bits because
    214     // conservative gathering uses the mark bits from our last mark pass to
    215     // determine whether a reference is valid.
    216     ConservativeRoots machineThreadRoots(this);
    217     m_machineThreads.gatherConservativeRoots(machineThreadRoots, &dummy);
    218 
    219     ConservativeRoots registerFileRoots(this);
    220     registerFile().gatherConservativeRoots(registerFileRoots);
    221 
    222     m_markedSpace.clearMarks();
    223 
    224     markStack.append(machineThreadRoots);
    225     markStack.drain();
    226 
    227     markStack.append(registerFileRoots);
    228     markStack.drain();
    229 
    230     markProtectedObjects(heapRootMarker);
    231     markStack.drain();
    232 
    233     markTempSortVectors(heapRootMarker);
    234     markStack.drain();
    235 
    236     if (m_markListSet && m_markListSet->size())
    237         MarkedArgumentBuffer::markLists(heapRootMarker, *m_markListSet);
    238     if (m_globalData->exception)
    239         heapRootMarker.mark(&m_globalData->exception);
    240     markStack.drain();
    241 
    242     m_handleHeap.markStrongHandles(heapRootMarker);
    243     markStack.drain();
    244 
    245     m_handleStack.mark(heapRootMarker);
    246     markStack.drain();
    247 
    248     // Mark the small strings cache as late as possible, since it will clear
    249     // itself if nothing else has marked it.
    250     // FIXME: Change the small strings cache to use Weak<T>.
    251     m_globalData->smallStrings.markChildren(heapRootMarker);
    252     markStack.drain();
    253 
    254     // Weak handles must be marked last, because their owners use the set of
    255     // opaque roots to determine reachability.
    256     int lastOpaqueRootCount;
    257     do {
    258         lastOpaqueRootCount = markStack.opaqueRootCount();
    259         m_handleHeap.markWeakHandles(heapRootMarker);
    260         markStack.drain();
    261     // If the set of opaque roots has grown, more weak handles may have become reachable.
    262     } while (lastOpaqueRootCount != markStack.opaqueRootCount());
    263 
    264     markStack.reset();
    265 
    266     m_operationInProgress = NoOperation;
    267 }
    268 
    269 size_t Heap::objectCount() const
    270 {
    271     return m_markedSpace.objectCount();
    272 }
    273 
    274 size_t Heap::size() const
    275 {
    276     return m_markedSpace.size();
    277 }
    278 
    279 size_t Heap::capacity() const
    280 {
    281     return m_markedSpace.capacity();
    282 }
    283 
    284 size_t Heap::globalObjectCount()
    285 {
    286     return m_globalData->globalObjectCount;
    287 }
    288 
    289 size_t Heap::protectedGlobalObjectCount()
    290 {
    291     size_t count = m_handleHeap.protectedGlobalObjectCount();
    292 
    293     ProtectCountSet::iterator end = m_protectedValues.end();
    294     for (ProtectCountSet::iterator it = m_protectedValues.begin(); it != end; ++it) {
    295         if (it->first->isObject() && asObject(it->first)->isGlobalObject())
    296             count++;
    297     }
    298 
    299     return count;
    300 }
    301 
    302 size_t Heap::protectedObjectCount()
    303 {
    304     return m_protectedValues.size();
    305 }
    306 
    307 class TypeCounter {
    308 public:
    309     TypeCounter();
    310     void operator()(JSCell*);
    311     PassOwnPtr<TypeCountSet> take();
    312 
    313 private:
    314     const char* typeName(JSCell*);
    315     OwnPtr<TypeCountSet> m_typeCountSet;
    316 };
    317 
    318 inline TypeCounter::TypeCounter()
    319     : m_typeCountSet(new TypeCountSet)
    320 {
    321 }
    322 
    323 inline const char* TypeCounter::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 (const ClassInfo* info = cell->classInfo())
    334         return info->className;
    335     if (!cell->isObject())
    336         return "[empty cell]";
    337     return "Object";
    338 }
    339 
    340 inline void TypeCounter::operator()(JSCell* cell)
    341 {
    342     m_typeCountSet->add(typeName(cell));
    343 }
    344 
    345 inline PassOwnPtr<TypeCountSet> TypeCounter::take()
    346 {
    347     return m_typeCountSet.release();
    348 }
    349 
    350 PassOwnPtr<TypeCountSet> Heap::protectedObjectTypeCounts()
    351 {
    352     TypeCounter typeCounter;
    353 
    354     ProtectCountSet::iterator end = m_protectedValues.end();
    355     for (ProtectCountSet::iterator it = m_protectedValues.begin(); it != end; ++it)
    356         typeCounter(it->first);
    357     m_handleHeap.protectedObjectTypeCounts(typeCounter);
    358 
    359     return typeCounter.take();
    360 }
    361 
    362 void HandleHeap::protectedObjectTypeCounts(TypeCounter& typeCounter)
    363 {
    364     Node* end = m_strongList.end();
    365     for (Node* node = m_strongList.begin(); node != end; node = node->next()) {
    366         JSValue value = *node->slot();
    367         if (value && value.isCell())
    368             typeCounter(value.asCell());
    369     }
    370 }
    371 
    372 PassOwnPtr<TypeCountSet> Heap::objectTypeCounts()
    373 {
    374     TypeCounter typeCounter;
    375     forEach(typeCounter);
    376     return typeCounter.take();
    377 }
    378 
    379 bool Heap::isBusy()
    380 {
    381     return m_operationInProgress != NoOperation;
    382 }
    383 
    384 void Heap::collectAllGarbage()
    385 {
    386     reset(DoSweep);
    387 }
    388 
    389 void Heap::reset(SweepToggle sweepToggle)
    390 {
    391     ASSERT(globalData()->identifierTable == wtfThreadData().currentIdentifierTable());
    392     JAVASCRIPTCORE_GC_BEGIN();
    393 
    394     markRoots();
    395     m_handleHeap.finalizeWeakHandles();
    396 
    397     JAVASCRIPTCORE_GC_MARKED();
    398 
    399     m_markedSpace.reset();
    400     m_extraCost = 0;
    401 
    402 #if ENABLE(JSC_ZOMBIES)
    403     sweepToggle = DoSweep;
    404 #endif
    405 
    406     if (sweepToggle == DoSweep) {
    407         m_markedSpace.sweep();
    408         m_markedSpace.shrink();
    409     }
    410 
    411     // To avoid pathological GC churn in large heaps, we set the allocation high
    412     // water mark to be proportional to the current size of the heap. The exact
    413     // proportion is a bit arbitrary. A 2X multiplier gives a 1:1 (heap size :
    414     // new bytes allocated) proportion, and seems to work well in benchmarks.
    415     size_t proportionalBytes = 2 * m_markedSpace.size();
    416     m_markedSpace.setHighWaterMark(max(proportionalBytes, minBytesPerCycle));
    417 
    418     JAVASCRIPTCORE_GC_END();
    419 
    420     (*m_activityCallback)();
    421 }
    422 
    423 void Heap::setActivityCallback(PassOwnPtr<GCActivityCallback> activityCallback)
    424 {
    425     m_activityCallback = activityCallback;
    426 }
    427 
    428 GCActivityCallback* Heap::activityCallback()
    429 {
    430     return m_activityCallback.get();
    431 }
    432 
    433 } // namespace JSC
    434