diff options
Diffstat (limited to 'Source/JavaScriptCore/heap/Heap.cpp')
-rw-r--r-- | Source/JavaScriptCore/heap/Heap.cpp | 433 |
1 files changed, 433 insertions, 0 deletions
diff --git a/Source/JavaScriptCore/heap/Heap.cpp b/Source/JavaScriptCore/heap/Heap.cpp new file mode 100644 index 0000000..11999b7 --- /dev/null +++ b/Source/JavaScriptCore/heap/Heap.cpp @@ -0,0 +1,433 @@ +/* + * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009 Apple Inc. All rights reserved. + * Copyright (C) 2007 Eric Seidel <eric@webkit.org> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#include "config.h" +#include "Heap.h" + +#include "CodeBlock.h" +#include "ConservativeRoots.h" +#include "GCActivityCallback.h" +#include "Interpreter.h" +#include "JSGlobalData.h" +#include "JSGlobalObject.h" +#include "JSLock.h" +#include "JSONObject.h" +#include "Tracing.h" +#include <algorithm> + +#define COLLECT_ON_EVERY_SLOW_ALLOCATION 0 + +using namespace std; + +namespace JSC { + +const size_t minBytesPerCycle = 512 * 1024; + +Heap::Heap(JSGlobalData* globalData) + : m_operationInProgress(NoOperation) + , m_markedSpace(globalData) + , m_markListSet(0) + , m_activityCallback(DefaultGCActivityCallback::create(this)) + , m_globalData(globalData) + , m_machineThreads(this) + , m_markStack(globalData->jsArrayVPtr) + , m_handleHeap(globalData) + , m_extraCost(0) +{ + m_markedSpace.setHighWaterMark(minBytesPerCycle); + (*m_activityCallback)(); +} + +Heap::~Heap() +{ + // The destroy function must already have been called, so assert this. + ASSERT(!m_globalData); +} + +void Heap::destroy() +{ + JSLock lock(SilenceAssertionsOnly); + + if (!m_globalData) + return; + + ASSERT(!m_globalData->dynamicGlobalObject); + ASSERT(m_operationInProgress == NoOperation); + + // The global object is not GC protected at this point, so sweeping may delete it + // (and thus the global data) before other objects that may use the global data. + RefPtr<JSGlobalData> protect(m_globalData); + +#if ENABLE(JIT) + m_globalData->jitStubs->clearHostFunctionStubs(); +#endif + + delete m_markListSet; + m_markListSet = 0; + m_markedSpace.clearMarks(); + m_handleHeap.finalizeWeakHandles(); + m_markedSpace.destroy(); + + m_globalData = 0; +} + +void Heap::reportExtraMemoryCostSlowCase(size_t cost) +{ + // Our frequency of garbage collection tries to balance memory use against speed + // by collecting based on the number of newly created values. However, for values + // that hold on to a great deal of memory that's not in the form of other JS values, + // that is not good enough - in some cases a lot of those objects can pile up and + // use crazy amounts of memory without a GC happening. So we track these extra + // memory costs. Only unusually large objects are noted, and we only keep track + // of this extra cost until the next GC. In garbage collected languages, most values + // are either very short lived temporaries, or have extremely long lifetimes. So + // if a large value survives one garbage collection, there is not much point to + // collecting more frequently as long as it stays alive. + + if (m_extraCost > maxExtraCost && m_extraCost > m_markedSpace.highWaterMark() / 2) + collectAllGarbage(); + m_extraCost += cost; +} + +void* Heap::allocateSlowCase(size_t bytes) +{ + ASSERT(globalData()->identifierTable == wtfThreadData().currentIdentifierTable()); + ASSERT(JSLock::lockCount() > 0); + ASSERT(JSLock::currentThreadIsHoldingLock()); + ASSERT(bytes <= MarkedSpace::maxCellSize); + ASSERT(m_operationInProgress == NoOperation); + +#if COLLECT_ON_EVERY_SLOW_ALLOCATION + collectAllGarbage(); + ASSERT(m_operationInProgress == NoOperation); +#endif + + reset(DoNotSweep); + + m_operationInProgress = Allocation; + void* result = m_markedSpace.allocate(bytes); + m_operationInProgress = NoOperation; + + ASSERT(result); + return result; +} + +void Heap::protect(JSValue k) +{ + ASSERT(k); + ASSERT(JSLock::currentThreadIsHoldingLock() || !m_globalData->isSharedInstance()); + + if (!k.isCell()) + return; + + m_protectedValues.add(k.asCell()); +} + +bool Heap::unprotect(JSValue k) +{ + ASSERT(k); + ASSERT(JSLock::currentThreadIsHoldingLock() || !m_globalData->isSharedInstance()); + + if (!k.isCell()) + return false; + + return m_protectedValues.remove(k.asCell()); +} + +void Heap::markProtectedObjects(HeapRootMarker& heapRootMarker) +{ + ProtectCountSet::iterator end = m_protectedValues.end(); + for (ProtectCountSet::iterator it = m_protectedValues.begin(); it != end; ++it) + heapRootMarker.mark(&it->first); +} + +void Heap::pushTempSortVector(Vector<ValueStringPair>* tempVector) +{ + m_tempSortingVectors.append(tempVector); +} + +void Heap::popTempSortVector(Vector<ValueStringPair>* tempVector) +{ + ASSERT_UNUSED(tempVector, tempVector == m_tempSortingVectors.last()); + m_tempSortingVectors.removeLast(); +} + +void Heap::markTempSortVectors(HeapRootMarker& heapRootMarker) +{ + typedef Vector<Vector<ValueStringPair>* > VectorOfValueStringVectors; + + VectorOfValueStringVectors::iterator end = m_tempSortingVectors.end(); + for (VectorOfValueStringVectors::iterator it = m_tempSortingVectors.begin(); it != end; ++it) { + Vector<ValueStringPair>* tempSortingVector = *it; + + Vector<ValueStringPair>::iterator vectorEnd = tempSortingVector->end(); + for (Vector<ValueStringPair>::iterator vectorIt = tempSortingVector->begin(); vectorIt != vectorEnd; ++vectorIt) { + if (vectorIt->first) + heapRootMarker.mark(&vectorIt->first); + } + } +} + +inline RegisterFile& Heap::registerFile() +{ + return m_globalData->interpreter->registerFile(); +} + +void Heap::markRoots() +{ +#ifndef NDEBUG + if (m_globalData->isSharedInstance()) { + ASSERT(JSLock::lockCount() > 0); + ASSERT(JSLock::currentThreadIsHoldingLock()); + } +#endif + + void* dummy; + + ASSERT(m_operationInProgress == NoOperation); + if (m_operationInProgress != NoOperation) + CRASH(); + + m_operationInProgress = Collection; + + MarkStack& markStack = m_markStack; + HeapRootMarker heapRootMarker(markStack); + + // We gather conservative roots before clearing mark bits because + // conservative gathering uses the mark bits from our last mark pass to + // determine whether a reference is valid. + ConservativeRoots machineThreadRoots(this); + m_machineThreads.gatherConservativeRoots(machineThreadRoots, &dummy); + + ConservativeRoots registerFileRoots(this); + registerFile().gatherConservativeRoots(registerFileRoots); + + m_markedSpace.clearMarks(); + + markStack.append(machineThreadRoots); + markStack.drain(); + + markStack.append(registerFileRoots); + markStack.drain(); + + markProtectedObjects(heapRootMarker); + markStack.drain(); + + markTempSortVectors(heapRootMarker); + markStack.drain(); + + if (m_markListSet && m_markListSet->size()) + MarkedArgumentBuffer::markLists(heapRootMarker, *m_markListSet); + if (m_globalData->exception) + heapRootMarker.mark(&m_globalData->exception); + markStack.drain(); + + m_handleHeap.markStrongHandles(heapRootMarker); + markStack.drain(); + + m_handleStack.mark(heapRootMarker); + markStack.drain(); + + // Mark the small strings cache as late as possible, since it will clear + // itself if nothing else has marked it. + // FIXME: Change the small strings cache to use Weak<T>. + m_globalData->smallStrings.markChildren(heapRootMarker); + markStack.drain(); + + // Weak handles must be marked last, because their owners use the set of + // opaque roots to determine reachability. + int lastOpaqueRootCount; + do { + lastOpaqueRootCount = markStack.opaqueRootCount(); + m_handleHeap.markWeakHandles(heapRootMarker); + markStack.drain(); + // If the set of opaque roots has grown, more weak handles may have become reachable. + } while (lastOpaqueRootCount != markStack.opaqueRootCount()); + + markStack.reset(); + + m_operationInProgress = NoOperation; +} + +size_t Heap::objectCount() const +{ + return m_markedSpace.objectCount(); +} + +size_t Heap::size() const +{ + return m_markedSpace.size(); +} + +size_t Heap::capacity() const +{ + return m_markedSpace.capacity(); +} + +size_t Heap::globalObjectCount() +{ + return m_globalData->globalObjectCount; +} + +size_t Heap::protectedGlobalObjectCount() +{ + size_t count = m_handleHeap.protectedGlobalObjectCount(); + + ProtectCountSet::iterator end = m_protectedValues.end(); + for (ProtectCountSet::iterator it = m_protectedValues.begin(); it != end; ++it) { + if (it->first->isObject() && asObject(it->first)->isGlobalObject()) + count++; + } + + return count; +} + +size_t Heap::protectedObjectCount() +{ + return m_protectedValues.size(); +} + +class TypeCounter { +public: + TypeCounter(); + void operator()(JSCell*); + PassOwnPtr<TypeCountSet> take(); + +private: + const char* typeName(JSCell*); + OwnPtr<TypeCountSet> m_typeCountSet; +}; + +inline TypeCounter::TypeCounter() + : m_typeCountSet(new TypeCountSet) +{ +} + +inline const char* TypeCounter::typeName(JSCell* cell) +{ + if (cell->isString()) + return "string"; + if (cell->isGetterSetter()) + return "Getter-Setter"; + if (cell->isAPIValueWrapper()) + return "API wrapper"; + if (cell->isPropertyNameIterator()) + return "For-in iterator"; + if (const ClassInfo* info = cell->classInfo()) + return info->className; + if (!cell->isObject()) + return "[empty cell]"; + return "Object"; +} + +inline void TypeCounter::operator()(JSCell* cell) +{ + m_typeCountSet->add(typeName(cell)); +} + +inline PassOwnPtr<TypeCountSet> TypeCounter::take() +{ + return m_typeCountSet.release(); +} + +PassOwnPtr<TypeCountSet> Heap::protectedObjectTypeCounts() +{ + TypeCounter typeCounter; + + ProtectCountSet::iterator end = m_protectedValues.end(); + for (ProtectCountSet::iterator it = m_protectedValues.begin(); it != end; ++it) + typeCounter(it->first); + m_handleHeap.protectedObjectTypeCounts(typeCounter); + + return typeCounter.take(); +} + +void HandleHeap::protectedObjectTypeCounts(TypeCounter& typeCounter) +{ + Node* end = m_strongList.end(); + for (Node* node = m_strongList.begin(); node != end; node = node->next()) { + JSValue value = *node->slot(); + if (value && value.isCell()) + typeCounter(value.asCell()); + } +} + +PassOwnPtr<TypeCountSet> Heap::objectTypeCounts() +{ + TypeCounter typeCounter; + forEach(typeCounter); + return typeCounter.take(); +} + +bool Heap::isBusy() +{ + return m_operationInProgress != NoOperation; +} + +void Heap::collectAllGarbage() +{ + reset(DoSweep); +} + +void Heap::reset(SweepToggle sweepToggle) +{ + ASSERT(globalData()->identifierTable == wtfThreadData().currentIdentifierTable()); + JAVASCRIPTCORE_GC_BEGIN(); + + markRoots(); + m_handleHeap.finalizeWeakHandles(); + + JAVASCRIPTCORE_GC_MARKED(); + + m_markedSpace.reset(); + m_extraCost = 0; + +#if ENABLE(JSC_ZOMBIES) + sweepToggle = DoSweep; +#endif + + if (sweepToggle == DoSweep) { + m_markedSpace.sweep(); + m_markedSpace.shrink(); + } + + // To avoid pathological GC churn in large heaps, we set the allocation high + // water mark to be proportional to the current size of the heap. The exact + // proportion is a bit arbitrary. A 2X multiplier gives a 1:1 (heap size : + // new bytes allocated) proportion, and seems to work well in benchmarks. + size_t proportionalBytes = 2 * m_markedSpace.size(); + m_markedSpace.setHighWaterMark(max(proportionalBytes, minBytesPerCycle)); + + JAVASCRIPTCORE_GC_END(); + + (*m_activityCallback)(); +} + +void Heap::setActivityCallback(PassOwnPtr<GCActivityCallback> activityCallback) +{ + m_activityCallback = activityCallback; +} + +GCActivityCallback* Heap::activityCallback() +{ + return m_activityCallback.get(); +} + +} // namespace JSC |