Index: Source/bindings/v8/V8GCController.cpp |
diff --git a/Source/bindings/v8/V8GCController.cpp b/Source/bindings/v8/V8GCController.cpp |
deleted file mode 100644 |
index f7c3f7ebbb44570fa7449c6518a983928a92e9b2..0000000000000000000000000000000000000000 |
--- a/Source/bindings/v8/V8GCController.cpp |
+++ /dev/null |
@@ -1,456 +0,0 @@ |
-/* |
- * Copyright (C) 2009 Google Inc. All rights reserved. |
- * |
- * Redistribution and use in source and binary forms, with or without |
- * modification, are permitted provided that the following conditions are |
- * met: |
- * |
- * * Redistributions of source code must retain the above copyright |
- * notice, this list of conditions and the following disclaimer. |
- * * Redistributions in binary form must reproduce the above |
- * copyright notice, this list of conditions and the following disclaimer |
- * in the documentation and/or other materials provided with the |
- * distribution. |
- * * Neither the name of Google Inc. nor the names of its |
- * contributors may be used to endorse or promote products derived from |
- * this software without specific prior written permission. |
- * |
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
- */ |
- |
-#include "config.h" |
-#include "bindings/v8/V8GCController.h" |
- |
-#include "bindings/core/v8/V8MutationObserver.h" |
-#include "bindings/core/v8/V8Node.h" |
-#include "bindings/v8/RetainedDOMInfo.h" |
-#include "bindings/v8/V8AbstractEventListener.h" |
-#include "bindings/v8/V8Binding.h" |
-#include "bindings/v8/V8ScriptRunner.h" |
-#include "bindings/v8/WrapperTypeInfo.h" |
-#include "core/dom/Attr.h" |
-#include "core/dom/Document.h" |
-#include "core/dom/NodeTraversal.h" |
-#include "core/dom/TemplateContentDocumentFragment.h" |
-#include "core/dom/shadow/ElementShadow.h" |
-#include "core/dom/shadow/ShadowRoot.h" |
-#include "core/html/HTMLImageElement.h" |
-#include "core/html/HTMLTemplateElement.h" |
-#include "core/html/imports/HTMLImportsController.h" |
-#include "core/inspector/InspectorTraceEvents.h" |
-#include "core/svg/SVGElement.h" |
-#include "platform/Partitions.h" |
-#include "platform/TraceEvent.h" |
-#include <algorithm> |
- |
-namespace WebCore { |
- |
-// FIXME: This should use opaque GC roots. |
-static void addReferencesForNodeWithEventListeners(v8::Isolate* isolate, Node* node, const v8::Persistent<v8::Object>& wrapper) |
-{ |
- ASSERT(node->hasEventListeners()); |
- |
- EventListenerIterator iterator(node); |
- while (EventListener* listener = iterator.nextListener()) { |
- if (listener->type() != EventListener::JSEventListenerType) |
- continue; |
- V8AbstractEventListener* v8listener = static_cast<V8AbstractEventListener*>(listener); |
- if (!v8listener->hasExistingListenerObject()) |
- continue; |
- |
- isolate->SetReference(wrapper, v8::Persistent<v8::Value>::Cast(v8listener->existingListenerObjectPersistentHandle())); |
- } |
-} |
- |
-Node* V8GCController::opaqueRootForGC(Node* node, v8::Isolate*) |
-{ |
- ASSERT(node); |
- // FIXME: Remove the special handling for image elements. |
- // The same special handling is in V8GCController::gcTree(). |
- // Maybe should image elements be active DOM nodes? |
- // See https://code.google.com/p/chromium/issues/detail?id=164882 |
- if (node->inDocument() || (isHTMLImageElement(*node) && toHTMLImageElement(*node).hasPendingActivity())) { |
- Document& document = node->document(); |
- if (HTMLImportsController* controller = document.importsController()) |
- return controller->master(); |
- return &document; |
- } |
- |
- if (node->isAttributeNode()) { |
- Node* ownerElement = toAttr(node)->ownerElement(); |
- if (!ownerElement) |
- return node; |
- node = ownerElement; |
- } |
- |
- while (Node* parent = node->parentOrShadowHostOrTemplateHostNode()) |
- node = parent; |
- |
- return node; |
-} |
- |
-// Regarding a minor GC algorithm for DOM nodes, see this document: |
-// https://docs.google.com/a/google.com/presentation/d/1uifwVYGNYTZDoGLyCb7sXa7g49mWNMW2gaWvMN5NLk8/edit#slide=id.p |
-class MinorGCWrapperVisitor : public v8::PersistentHandleVisitor { |
-public: |
- explicit MinorGCWrapperVisitor(v8::Isolate* isolate) |
- : m_isolate(isolate) |
- { } |
- |
- virtual void VisitPersistentHandle(v8::Persistent<v8::Value>* value, uint16_t classId) OVERRIDE |
- { |
- // A minor DOM GC can collect only Nodes. |
- if (classId != v8DOMNodeClassId) |
- return; |
- |
- // To make minor GC cycle time bounded, we limit the number of wrappers handled |
- // by each minor GC cycle to 10000. This value was selected so that the minor |
- // GC cycle time is bounded to 20 ms in a case where the new space size |
- // is 16 MB and it is full of wrappers (which is almost the worst case). |
- // Practically speaking, as far as I crawled real web applications, |
- // the number of wrappers handled by each minor GC cycle is at most 3000. |
- // So this limit is mainly for pathological micro benchmarks. |
- const unsigned wrappersHandledByEachMinorGC = 10000; |
- if (m_nodesInNewSpace.size() >= wrappersHandledByEachMinorGC) |
- return; |
- |
- // Casting to a Handle is safe here, since the Persistent doesn't get GCd |
- // during the GC prologue. |
- ASSERT((*reinterpret_cast<v8::Handle<v8::Value>*>(value))->IsObject()); |
- v8::Handle<v8::Object>* wrapper = reinterpret_cast<v8::Handle<v8::Object>*>(value); |
- ASSERT(V8DOMWrapper::isDOMWrapper(*wrapper)); |
- ASSERT(V8Node::hasInstance(*wrapper, m_isolate)); |
- Node* node = V8Node::toNative(*wrapper); |
- // A minor DOM GC can handle only node wrappers in the main world. |
- // Note that node->wrapper().IsEmpty() returns true for nodes that |
- // do not have wrappers in the main world. |
- if (node->containsWrapper()) { |
- const WrapperTypeInfo* type = toWrapperTypeInfo(*wrapper); |
- ActiveDOMObject* activeDOMObject = type->toActiveDOMObject(*wrapper); |
- if (activeDOMObject && activeDOMObject->hasPendingActivity()) |
- return; |
- // FIXME: Remove the special handling for image elements. |
- // The same special handling is in V8GCController::opaqueRootForGC(). |
- // Maybe should image elements be active DOM nodes? |
- // See https://code.google.com/p/chromium/issues/detail?id=164882 |
- if (isHTMLImageElement(*node) && toHTMLImageElement(*node).hasPendingActivity()) |
- return; |
- // FIXME: Remove the special handling for SVG context elements. |
- if (node->isSVGElement() && toSVGElement(node)->isContextElement()) |
- return; |
- |
- m_nodesInNewSpace.append(node); |
- node->markV8CollectableDuringMinorGC(); |
- } |
- } |
- |
- void notifyFinished() |
- { |
- Node** nodeIterator = m_nodesInNewSpace.begin(); |
- Node** nodeIteratorEnd = m_nodesInNewSpace.end(); |
- for (; nodeIterator < nodeIteratorEnd; ++nodeIterator) { |
- Node* node = *nodeIterator; |
- ASSERT(node->containsWrapper()); |
- if (node->isV8CollectableDuringMinorGC()) { // This branch is just for performance. |
- gcTree(m_isolate, node); |
- node->clearV8CollectableDuringMinorGC(); |
- } |
- } |
- } |
- |
-private: |
- bool traverseTree(Node* rootNode, Vector<Node*, initialNodeVectorSize>* partiallyDependentNodes) |
- { |
- // To make each minor GC time bounded, we might need to give up |
- // traversing at some point for a large DOM tree. That being said, |
- // I could not observe the need even in pathological test cases. |
- for (Node* node = rootNode; node; node = NodeTraversal::next(*node)) { |
- if (node->containsWrapper()) { |
- if (!node->isV8CollectableDuringMinorGC()) { |
- // This node is not in the new space of V8. This indicates that |
- // the minor GC cannot anyway judge reachability of this DOM tree. |
- // Thus we give up traversing the DOM tree. |
- return false; |
- } |
- node->clearV8CollectableDuringMinorGC(); |
- partiallyDependentNodes->append(node); |
- } |
- if (ShadowRoot* shadowRoot = node->youngestShadowRoot()) { |
- if (!traverseTree(shadowRoot, partiallyDependentNodes)) |
- return false; |
- } else if (node->isShadowRoot()) { |
- if (ShadowRoot* shadowRoot = toShadowRoot(node)->olderShadowRoot()) { |
- if (!traverseTree(shadowRoot, partiallyDependentNodes)) |
- return false; |
- } |
- } |
- // <template> has a |content| property holding a DOM fragment which we must traverse, |
- // just like we do for the shadow trees above. |
- if (isHTMLTemplateElement(*node)) { |
- if (!traverseTree(toHTMLTemplateElement(*node).content(), partiallyDependentNodes)) |
- return false; |
- } |
- |
- // Document maintains the list of imported documents through HTMLImportsController. |
- if (node->isDocumentNode()) { |
- Document* document = toDocument(node); |
- HTMLImportsController* controller = document->importsController(); |
- if (controller && document == controller->master()) { |
- for (unsigned i = 0; i < controller->loaderCount(); ++i) { |
- if (!traverseTree(controller->loaderDocumentAt(i), partiallyDependentNodes)) |
- return false; |
- } |
- } |
- } |
- } |
- return true; |
- } |
- |
- void gcTree(v8::Isolate* isolate, Node* startNode) |
- { |
- Vector<Node*, initialNodeVectorSize> partiallyDependentNodes; |
- |
- Node* node = startNode; |
- while (Node* parent = node->parentOrShadowHostOrTemplateHostNode()) |
- node = parent; |
- |
- if (!traverseTree(node, &partiallyDependentNodes)) |
- return; |
- |
- // We completed the DOM tree traversal. All wrappers in the DOM tree are |
- // stored in partiallyDependentNodes and are expected to exist in the new space of V8. |
- // We report those wrappers to V8 as an object group. |
- Node** nodeIterator = partiallyDependentNodes.begin(); |
- Node** const nodeIteratorEnd = partiallyDependentNodes.end(); |
- if (nodeIterator == nodeIteratorEnd) |
- return; |
- |
- Node* groupRoot = *nodeIterator; |
- for (; nodeIterator != nodeIteratorEnd; ++nodeIterator) { |
- (*nodeIterator)->markAsDependentGroup(groupRoot, isolate); |
- } |
- } |
- |
- Vector<Node*> m_nodesInNewSpace; |
- v8::Isolate* m_isolate; |
-}; |
- |
-class MajorGCWrapperVisitor : public v8::PersistentHandleVisitor { |
-public: |
- explicit MajorGCWrapperVisitor(v8::Isolate* isolate, bool constructRetainedObjectInfos) |
- : m_isolate(isolate) |
- , m_liveRootGroupIdSet(false) |
- , m_constructRetainedObjectInfos(constructRetainedObjectInfos) |
- { |
- } |
- |
- virtual void VisitPersistentHandle(v8::Persistent<v8::Value>* value, uint16_t classId) OVERRIDE |
- { |
- if (classId != v8DOMNodeClassId && classId != v8DOMObjectClassId) |
- return; |
- |
- // Casting to a Handle is safe here, since the Persistent doesn't get GCd |
- // during the GC prologue. |
- ASSERT((*reinterpret_cast<v8::Handle<v8::Value>*>(value))->IsObject()); |
- v8::Handle<v8::Object>* wrapper = reinterpret_cast<v8::Handle<v8::Object>*>(value); |
- ASSERT(V8DOMWrapper::isDOMWrapper(*wrapper)); |
- |
- if (value->IsIndependent()) |
- return; |
- |
- const WrapperTypeInfo* type = toWrapperTypeInfo(*wrapper); |
- void* object = toNative(*wrapper); |
- |
- ActiveDOMObject* activeDOMObject = type->toActiveDOMObject(*wrapper); |
- if (activeDOMObject && activeDOMObject->hasPendingActivity()) |
- m_isolate->SetObjectGroupId(*value, liveRootId()); |
- |
- if (classId == v8DOMNodeClassId) { |
- ASSERT(V8Node::hasInstance(*wrapper, m_isolate)); |
- Node* node = static_cast<Node*>(object); |
- if (node->hasEventListeners()) |
- addReferencesForNodeWithEventListeners(m_isolate, node, v8::Persistent<v8::Object>::Cast(*value)); |
- Node* root = V8GCController::opaqueRootForGC(node, m_isolate); |
- m_isolate->SetObjectGroupId(*value, v8::UniqueId(reinterpret_cast<intptr_t>(root))); |
- if (m_constructRetainedObjectInfos) |
- m_groupsWhichNeedRetainerInfo.append(root); |
- } else if (classId == v8DOMObjectClassId) { |
- type->visitDOMWrapper(object, v8::Persistent<v8::Object>::Cast(*value), m_isolate); |
- } else { |
- ASSERT_NOT_REACHED(); |
- } |
- } |
- |
- void notifyFinished() |
- { |
- if (!m_constructRetainedObjectInfos) |
- return; |
- std::sort(m_groupsWhichNeedRetainerInfo.begin(), m_groupsWhichNeedRetainerInfo.end()); |
- Node* alreadyAdded = 0; |
- v8::HeapProfiler* profiler = m_isolate->GetHeapProfiler(); |
- for (size_t i = 0; i < m_groupsWhichNeedRetainerInfo.size(); ++i) { |
- Node* root = m_groupsWhichNeedRetainerInfo[i]; |
- if (root != alreadyAdded) { |
- profiler->SetRetainedObjectInfo(v8::UniqueId(reinterpret_cast<intptr_t>(root)), new RetainedDOMInfo(root)); |
- alreadyAdded = root; |
- } |
- } |
- } |
- |
-private: |
- v8::UniqueId liveRootId() |
- { |
- const v8::Persistent<v8::Value>& liveRoot = V8PerIsolateData::from(m_isolate)->ensureLiveRoot(); |
- const intptr_t* idPointer = reinterpret_cast<const intptr_t*>(&liveRoot); |
- v8::UniqueId id(*idPointer); |
- if (!m_liveRootGroupIdSet) { |
- m_isolate->SetObjectGroupId(liveRoot, id); |
- m_liveRootGroupIdSet = true; |
- } |
- return id; |
- } |
- |
- v8::Isolate* m_isolate; |
- Vector<Node*> m_groupsWhichNeedRetainerInfo; |
- bool m_liveRootGroupIdSet; |
- bool m_constructRetainedObjectInfos; |
-}; |
- |
-static unsigned long long usedHeapSize(v8::Isolate* isolate) |
-{ |
- v8::HeapStatistics heapStatistics; |
- isolate->GetHeapStatistics(&heapStatistics); |
- return heapStatistics.used_heap_size(); |
-} |
- |
-void V8GCController::gcPrologue(v8::GCType type, v8::GCCallbackFlags flags) |
-{ |
- // FIXME: It would be nice if the GC callbacks passed the Isolate directly.... |
- v8::Isolate* isolate = v8::Isolate::GetCurrent(); |
- TRACE_EVENT_BEGIN1(TRACE_DISABLED_BY_DEFAULT("devtools.timeline"), "GCEvent", "usedHeapSizeBefore", usedHeapSize(isolate)); |
- if (type == v8::kGCTypeScavenge) |
- minorGCPrologue(isolate); |
- else if (type == v8::kGCTypeMarkSweepCompact) |
- majorGCPrologue(flags & v8::kGCCallbackFlagConstructRetainedObjectInfos, isolate); |
-} |
- |
-void V8GCController::minorGCPrologue(v8::Isolate* isolate) |
-{ |
- TRACE_EVENT_BEGIN0("v8", "minorGC"); |
- if (isMainThread()) { |
- { |
- TRACE_EVENT_SCOPED_SAMPLING_STATE("blink", "DOMMinorGC"); |
- v8::HandleScope scope(isolate); |
- MinorGCWrapperVisitor visitor(isolate); |
- v8::V8::VisitHandlesForPartialDependence(isolate, &visitor); |
- visitor.notifyFinished(); |
- } |
- V8PerIsolateData::from(isolate)->setPreviousSamplingState(TRACE_EVENT_GET_SAMPLING_STATE()); |
- TRACE_EVENT_SET_SAMPLING_STATE("v8", "V8MinorGC"); |
- } |
-} |
- |
-// Create object groups for DOM tree nodes. |
-void V8GCController::majorGCPrologue(bool constructRetainedObjectInfos, v8::Isolate* isolate) |
-{ |
- v8::HandleScope scope(isolate); |
- TRACE_EVENT_BEGIN0("v8", "majorGC"); |
- if (isMainThread()) { |
- { |
- TRACE_EVENT_SCOPED_SAMPLING_STATE("blink", "DOMMajorGC"); |
- MajorGCWrapperVisitor visitor(isolate, constructRetainedObjectInfos); |
- v8::V8::VisitHandlesWithClassIds(&visitor); |
- visitor.notifyFinished(); |
- } |
- V8PerIsolateData::from(isolate)->setPreviousSamplingState(TRACE_EVENT_GET_SAMPLING_STATE()); |
- TRACE_EVENT_SET_SAMPLING_STATE("v8", "V8MajorGC"); |
- } else { |
- MajorGCWrapperVisitor visitor(isolate, constructRetainedObjectInfos); |
- v8::V8::VisitHandlesWithClassIds(&visitor); |
- visitor.notifyFinished(); |
- } |
-} |
- |
-void V8GCController::gcEpilogue(v8::GCType type, v8::GCCallbackFlags flags) |
-{ |
- // FIXME: It would be nice if the GC callbacks passed the Isolate directly.... |
- v8::Isolate* isolate = v8::Isolate::GetCurrent(); |
- if (type == v8::kGCTypeScavenge) |
- minorGCEpilogue(isolate); |
- else if (type == v8::kGCTypeMarkSweepCompact) |
- majorGCEpilogue(isolate); |
- |
- // Forces a Blink heap garbage collection when a garbage collection |
- // was forced from V8. This is used for tests that force GCs from |
- // JavaScript to verify that objects die when expected. |
- if (flags & v8::kGCCallbackFlagForced) { |
- // This single GC is not enough for two reasons: |
- // (1) The GC is not precise because the GC scans on-stack pointers conservatively. |
- // (2) One GC is not enough to break a chain of persistent handles. It's possible that |
- // some heap allocated objects own objects that contain persistent handles |
- // pointing to other heap allocated objects. To break the chain, we need multiple GCs. |
- // |
- // Regarding (1), we force a precise GC at the end of the current event loop. So if you want |
- // to collect all garbage, you need to wait until the next event loop. |
- // Regarding (2), it would be OK in practice to trigger only one GC per gcEpilogue, because |
- // GCController.collectAll() forces 7 V8's GC. |
- Heap::collectGarbage(ThreadState::HeapPointersOnStack); |
- |
- // Forces a precise GC at the end of the current event loop. |
- Heap::setForcePreciseGCForTesting(); |
- } |
- |
- TRACE_EVENT_END1(TRACE_DISABLED_BY_DEFAULT("devtools.timeline"), "GCEvent", "usedHeapSizeAfter", usedHeapSize(isolate)); |
- TRACE_EVENT_INSTANT1(TRACE_DISABLED_BY_DEFAULT("devtools.timeline"), "UpdateCounters", "data", InspectorUpdateCountersEvent::data()); |
-} |
- |
-void V8GCController::minorGCEpilogue(v8::Isolate* isolate) |
-{ |
- TRACE_EVENT_END0("v8", "minorGC"); |
- if (isMainThread()) |
- TRACE_EVENT_SET_NONCONST_SAMPLING_STATE(V8PerIsolateData::from(isolate)->previousSamplingState()); |
-} |
- |
-void V8GCController::majorGCEpilogue(v8::Isolate* isolate) |
-{ |
- v8::HandleScope scope(isolate); |
- |
- TRACE_EVENT_END0("v8", "majorGC"); |
- if (isMainThread()) |
- TRACE_EVENT_SET_NONCONST_SAMPLING_STATE(V8PerIsolateData::from(isolate)->previousSamplingState()); |
-} |
- |
-void V8GCController::collectGarbage(v8::Isolate* isolate) |
-{ |
- v8::HandleScope handleScope(isolate); |
- RefPtr<ScriptState> scriptState = ScriptState::create(v8::Context::New(isolate), DOMWrapperWorld::create()); |
- ScriptState::Scope scope(scriptState.get()); |
- V8ScriptRunner::compileAndRunInternalScript(v8String(isolate, "if (gc) gc();"), isolate); |
- scriptState->disposePerContextData(); |
-} |
- |
-void V8GCController::reportDOMMemoryUsageToV8(v8::Isolate* isolate) |
-{ |
- if (!isMainThread()) |
- return; |
- |
- static size_t lastUsageReportedToV8 = 0; |
- |
- size_t currentUsage = Partitions::currentDOMMemoryUsage(); |
- int64_t diff = static_cast<int64_t>(currentUsage) - static_cast<int64_t>(lastUsageReportedToV8); |
- isolate->AdjustAmountOfExternalAllocatedMemory(diff); |
- |
- lastUsageReportedToV8 = currentUsage; |
-} |
- |
-} // namespace WebCore |