diff options
author | Ben Murdoch <benm@google.com> | 2011-05-13 16:23:25 +0100 |
---|---|---|
committer | Ben Murdoch <benm@google.com> | 2011-05-16 11:35:02 +0100 |
commit | 65f03d4f644ce73618e5f4f50dd694b26f55ae12 (patch) | |
tree | f478babb801e720de7bfaee23443ffe029f58731 /Source/JavaScriptCore/runtime/Heap.cpp | |
parent | 47de4a2fb7262c7ebdb9cd133ad2c54c187454d0 (diff) | |
download | external_webkit-65f03d4f644ce73618e5f4f50dd694b26f55ae12.zip external_webkit-65f03d4f644ce73618e5f4f50dd694b26f55ae12.tar.gz external_webkit-65f03d4f644ce73618e5f4f50dd694b26f55ae12.tar.bz2 |
Merge WebKit at r75993: Initial merge by git.
Change-Id: I602bbdc3974787a3b0450456a30a7868286921c3
Diffstat (limited to 'Source/JavaScriptCore/runtime/Heap.cpp')
-rw-r--r-- | Source/JavaScriptCore/runtime/Heap.cpp | 434 |
1 files changed, 434 insertions, 0 deletions
diff --git a/Source/JavaScriptCore/runtime/Heap.cpp b/Source/JavaScriptCore/runtime/Heap.cpp new file mode 100644 index 0000000..a224ee0 --- /dev/null +++ b/Source/JavaScriptCore/runtime/Heap.cpp @@ -0,0 +1,434 @@ +/* + * 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 "CollectorHeapIterator.h" +#include "GCActivityCallback.h" +#include "GCHandle.h" +#include "Interpreter.h" +#include "JSGlobalData.h" +#include "JSGlobalObject.h" +#include "JSLock.h" +#include "JSONObject.h" +#include "Tracing.h" + +#define COLLECT_ON_EVERY_ALLOCATION 0 + +namespace JSC { + +Heap::Heap(JSGlobalData* globalData) + : m_markedSpace(globalData) + , m_operationInProgress(NoOperation) + , m_markListSet(0) + , m_activityCallback(DefaultGCActivityCallback::create(this)) + , m_globalData(globalData) + , m_machineStackMarker(&globalData->heap) + , m_extraCost(0) +{ + (*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); + + delete m_markListSet; + m_markListSet = 0; + + ProtectCountSet protectedValuesCopy = m_protectedValues; + m_markedSpace.destroy(protectedValuesCopy); + ASSERT(!protectedObjectCount()); + + m_globalData = 0; +} + +void Heap::recordExtraCost(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.size() / 2) { + JAVASCRIPTCORE_GC_BEGIN(); + + // If the last iteration through the heap deallocated blocks, we need + // to clean up remaining garbage before marking. Otherwise, the conservative + // marking mechanism might follow a pointer to unmapped memory. + if (m_markedSpace.didShrink()) + m_markedSpace.sweep(); + + markRoots(); + + JAVASCRIPTCORE_GC_MARKED(); + + m_markedSpace.reset(); + m_extraCost = 0; + + JAVASCRIPTCORE_GC_END(); + + (*m_activityCallback)(); + } + m_extraCost += cost; +} + +void* Heap::allocate(size_t s) +{ + ASSERT(globalData()->identifierTable == wtfThreadData().currentIdentifierTable()); + ASSERT(JSLock::lockCount() > 0); + ASSERT(JSLock::currentThreadIsHoldingLock()); + ASSERT_UNUSED(s, s <= HeapConstants::cellSize); + ASSERT(m_operationInProgress == NoOperation); + +#if COLLECT_ON_EVERY_ALLOCATION + collectAllGarbage(); + ASSERT(m_operationInProgress == NoOperation); +#endif + + m_operationInProgress = Allocation; + void* result = m_markedSpace.allocate(s); + m_operationInProgress = NoOperation; + + if (!result) { + JAVASCRIPTCORE_GC_BEGIN(); + + markRoots(); + + JAVASCRIPTCORE_GC_MARKED(); + + m_markedSpace.reset(); + m_extraCost = 0; + + JAVASCRIPTCORE_GC_END(); + + (*m_activityCallback)(); + + m_operationInProgress = Allocation; + result = m_markedSpace.allocate(s); + m_operationInProgress = NoOperation; + } + ASSERT(result); + return result; +} + +void Heap::markConservatively(MarkStack& markStack, void* start, void* end) +{ + m_markedSpace.markConservatively(markStack, start, end); +} + +void Heap::updateWeakGCHandles() +{ + for (unsigned i = 0; i < m_weakGCHandlePools.size(); ++i) + weakGCHandlePool(i)->update(); +} + +void WeakGCHandlePool::update() +{ + for (unsigned i = 1; i < WeakGCHandlePool::numPoolEntries; ++i) { + if (m_entries[i].isValidPtr()) { + JSCell* cell = m_entries[i].get(); + if (!cell || !Heap::isCellMarked(cell)) + m_entries[i].invalidate(); + } + } +} + +WeakGCHandle* Heap::addWeakGCHandle(JSCell* ptr) +{ + for (unsigned i = 0; i < m_weakGCHandlePools.size(); ++i) + if (!weakGCHandlePool(i)->isFull()) + return weakGCHandlePool(i)->allocate(ptr); + + PageAllocationAligned allocation = PageAllocationAligned::allocate(WeakGCHandlePool::poolSize, WeakGCHandlePool::poolSize, OSAllocator::JSGCHeapPages); + m_weakGCHandlePools.append(allocation); + + WeakGCHandlePool* pool = new (allocation.base()) WeakGCHandlePool(); + return pool->allocate(ptr); +} + +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(MarkStack& markStack) +{ + ProtectCountSet::iterator end = m_protectedValues.end(); + for (ProtectCountSet::iterator it = m_protectedValues.begin(); it != end; ++it) { + markStack.append(it->first); + markStack.drain(); + } +} + +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(MarkStack& markStack) +{ + 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) + markStack.append(vectorIt->first); + markStack.drain(); + } +} + +void Heap::markRoots() +{ +#ifndef NDEBUG + if (m_globalData->isSharedInstance()) { + ASSERT(JSLock::lockCount() > 0); + ASSERT(JSLock::currentThreadIsHoldingLock()); + } +#endif + + ASSERT(m_operationInProgress == NoOperation); + if (m_operationInProgress != NoOperation) + CRASH(); + + m_operationInProgress = Collection; + + MarkStack& markStack = m_globalData->markStack; + + // Reset mark bits. + m_markedSpace.clearMarkBits(); + + // Mark stack roots. + m_machineStackMarker.markMachineStackConservatively(markStack); + m_globalData->interpreter->registerFile().markCallFrames(markStack, this); + + // Mark explicitly registered roots. + markProtectedObjects(markStack); + + // Mark temporary vector for Array sorting + markTempSortVectors(markStack); + + // Mark misc. other roots. + if (m_markListSet && m_markListSet->size()) + MarkedArgumentBuffer::markLists(markStack, *m_markListSet); + if (m_globalData->exception) + markStack.append(m_globalData->exception); + if (m_globalData->firstStringifierToMark) + JSONObject::markStringifiers(markStack, m_globalData->firstStringifierToMark); + + // Mark the small strings cache last, since it will clear itself if nothing + // else has marked it. + m_globalData->smallStrings.markChildren(markStack); + + markStack.drain(); + markStack.compact(); + + updateWeakGCHandles(); + + m_operationInProgress = NoOperation; +} + +size_t Heap::objectCount() const +{ + return m_markedSpace.objectCount(); +} + +MarkedSpace::Statistics Heap::statistics() const +{ + return m_markedSpace.statistics(); +} + +size_t Heap::size() const +{ + return m_markedSpace.size(); +} + +size_t Heap::globalObjectCount() +{ + size_t count = 0; + if (JSGlobalObject* head = m_globalData->head) { + JSGlobalObject* o = head; + do { + ++count; + o = o->next(); + } while (o != head); + } + return count; +} + +size_t Heap::protectedGlobalObjectCount() +{ + size_t count = 0; + if (JSGlobalObject* head = m_globalData->head) { + JSGlobalObject* o = head; + do { + if (m_protectedValues.contains(o)) + ++count; + o = o->next(); + } while (o != head); + } + + return count; +} + +size_t Heap::protectedObjectCount() +{ + return m_protectedValues.size(); +} + +static const char* 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 (!cell->isObject()) + return "[empty cell]"; + const ClassInfo* info = cell->classInfo(); + return info ? info->className : "Object"; +} + +HashCountedSet<const char*>* Heap::protectedObjectTypeCounts() +{ + HashCountedSet<const char*>* counts = new HashCountedSet<const char*>; + + ProtectCountSet::iterator end = m_protectedValues.end(); + for (ProtectCountSet::iterator it = m_protectedValues.begin(); it != end; ++it) + counts->add(typeName(it->first)); + + return counts; +} + +HashCountedSet<const char*>* Heap::objectTypeCounts() +{ + HashCountedSet<const char*>* counts = new HashCountedSet<const char*>; + + LiveObjectIterator it = primaryHeapBegin(); + LiveObjectIterator heapEnd = primaryHeapEnd(); + for ( ; it != heapEnd; ++it) + counts->add(typeName(*it)); + + return counts; +} + +bool Heap::isBusy() +{ + return m_operationInProgress != NoOperation; +} + +void Heap::collectAllGarbage() +{ + ASSERT(globalData()->identifierTable == wtfThreadData().currentIdentifierTable()); + JAVASCRIPTCORE_GC_BEGIN(); + + // If the last iteration through the heap deallocated blocks, we need + // to clean up remaining garbage before marking. Otherwise, the conservative + // marking mechanism might follow a pointer to unmapped memory. + if (m_markedSpace.didShrink()) + m_markedSpace.sweep(); + + markRoots(); + + JAVASCRIPTCORE_GC_MARKED(); + + m_markedSpace.reset(); + m_markedSpace.sweep(); + m_extraCost = 0; + + JAVASCRIPTCORE_GC_END(); + + (*m_activityCallback)(); +} + +LiveObjectIterator Heap::primaryHeapBegin() +{ + return m_markedSpace.primaryHeapBegin(); +} + +LiveObjectIterator Heap::primaryHeapEnd() +{ + return m_markedSpace.primaryHeapEnd(); +} + +void Heap::setActivityCallback(PassOwnPtr<GCActivityCallback> activityCallback) +{ + m_activityCallback = activityCallback; +} + +GCActivityCallback* Heap::activityCallback() +{ + return m_activityCallback.get(); +} + +} // namespace JSC |