Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(303)

Side by Side Diff: Source/bindings/v8/V8GCController.cpp

Issue 351423002: Moved files under Source/bindings/v8 to Source/bindings/core/v8. (Closed) Base URL: svn://svn.chromium.org/blink/trunk
Patch Set: Created 6 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « Source/bindings/v8/V8GCController.h ('k') | Source/bindings/v8/V8GCForContextDispose.h » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 /*
2 * Copyright (C) 2009 Google Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
6 * met:
7 *
8 * * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above
11 * copyright notice, this list of conditions and the following disclaimer
12 * in the documentation and/or other materials provided with the
13 * distribution.
14 * * Neither the name of Google Inc. nor the names of its
15 * contributors may be used to endorse or promote products derived from
16 * this software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 */
30
31 #include "config.h"
32 #include "bindings/v8/V8GCController.h"
33
34 #include "bindings/core/v8/V8MutationObserver.h"
35 #include "bindings/core/v8/V8Node.h"
36 #include "bindings/v8/RetainedDOMInfo.h"
37 #include "bindings/v8/V8AbstractEventListener.h"
38 #include "bindings/v8/V8Binding.h"
39 #include "bindings/v8/V8ScriptRunner.h"
40 #include "bindings/v8/WrapperTypeInfo.h"
41 #include "core/dom/Attr.h"
42 #include "core/dom/Document.h"
43 #include "core/dom/NodeTraversal.h"
44 #include "core/dom/TemplateContentDocumentFragment.h"
45 #include "core/dom/shadow/ElementShadow.h"
46 #include "core/dom/shadow/ShadowRoot.h"
47 #include "core/html/HTMLImageElement.h"
48 #include "core/html/HTMLTemplateElement.h"
49 #include "core/html/imports/HTMLImportsController.h"
50 #include "core/inspector/InspectorTraceEvents.h"
51 #include "core/svg/SVGElement.h"
52 #include "platform/Partitions.h"
53 #include "platform/TraceEvent.h"
54 #include <algorithm>
55
56 namespace WebCore {
57
58 // FIXME: This should use opaque GC roots.
59 static void addReferencesForNodeWithEventListeners(v8::Isolate* isolate, Node* n ode, const v8::Persistent<v8::Object>& wrapper)
60 {
61 ASSERT(node->hasEventListeners());
62
63 EventListenerIterator iterator(node);
64 while (EventListener* listener = iterator.nextListener()) {
65 if (listener->type() != EventListener::JSEventListenerType)
66 continue;
67 V8AbstractEventListener* v8listener = static_cast<V8AbstractEventListene r*>(listener);
68 if (!v8listener->hasExistingListenerObject())
69 continue;
70
71 isolate->SetReference(wrapper, v8::Persistent<v8::Value>::Cast(v8listene r->existingListenerObjectPersistentHandle()));
72 }
73 }
74
75 Node* V8GCController::opaqueRootForGC(Node* node, v8::Isolate*)
76 {
77 ASSERT(node);
78 // FIXME: Remove the special handling for image elements.
79 // The same special handling is in V8GCController::gcTree().
80 // Maybe should image elements be active DOM nodes?
81 // See https://code.google.com/p/chromium/issues/detail?id=164882
82 if (node->inDocument() || (isHTMLImageElement(*node) && toHTMLImageElement(* node).hasPendingActivity())) {
83 Document& document = node->document();
84 if (HTMLImportsController* controller = document.importsController())
85 return controller->master();
86 return &document;
87 }
88
89 if (node->isAttributeNode()) {
90 Node* ownerElement = toAttr(node)->ownerElement();
91 if (!ownerElement)
92 return node;
93 node = ownerElement;
94 }
95
96 while (Node* parent = node->parentOrShadowHostOrTemplateHostNode())
97 node = parent;
98
99 return node;
100 }
101
102 // Regarding a minor GC algorithm for DOM nodes, see this document:
103 // https://docs.google.com/a/google.com/presentation/d/1uifwVYGNYTZDoGLyCb7sXa7g 49mWNMW2gaWvMN5NLk8/edit#slide=id.p
104 class MinorGCWrapperVisitor : public v8::PersistentHandleVisitor {
105 public:
106 explicit MinorGCWrapperVisitor(v8::Isolate* isolate)
107 : m_isolate(isolate)
108 { }
109
110 virtual void VisitPersistentHandle(v8::Persistent<v8::Value>* value, uint16_ t classId) OVERRIDE
111 {
112 // A minor DOM GC can collect only Nodes.
113 if (classId != v8DOMNodeClassId)
114 return;
115
116 // To make minor GC cycle time bounded, we limit the number of wrappers handled
117 // by each minor GC cycle to 10000. This value was selected so that the minor
118 // GC cycle time is bounded to 20 ms in a case where the new space size
119 // is 16 MB and it is full of wrappers (which is almost the worst case).
120 // Practically speaking, as far as I crawled real web applications,
121 // the number of wrappers handled by each minor GC cycle is at most 3000 .
122 // So this limit is mainly for pathological micro benchmarks.
123 const unsigned wrappersHandledByEachMinorGC = 10000;
124 if (m_nodesInNewSpace.size() >= wrappersHandledByEachMinorGC)
125 return;
126
127 // Casting to a Handle is safe here, since the Persistent doesn't get GC d
128 // during the GC prologue.
129 ASSERT((*reinterpret_cast<v8::Handle<v8::Value>*>(value))->IsObject());
130 v8::Handle<v8::Object>* wrapper = reinterpret_cast<v8::Handle<v8::Object >*>(value);
131 ASSERT(V8DOMWrapper::isDOMWrapper(*wrapper));
132 ASSERT(V8Node::hasInstance(*wrapper, m_isolate));
133 Node* node = V8Node::toNative(*wrapper);
134 // A minor DOM GC can handle only node wrappers in the main world.
135 // Note that node->wrapper().IsEmpty() returns true for nodes that
136 // do not have wrappers in the main world.
137 if (node->containsWrapper()) {
138 const WrapperTypeInfo* type = toWrapperTypeInfo(*wrapper);
139 ActiveDOMObject* activeDOMObject = type->toActiveDOMObject(*wrapper) ;
140 if (activeDOMObject && activeDOMObject->hasPendingActivity())
141 return;
142 // FIXME: Remove the special handling for image elements.
143 // The same special handling is in V8GCController::opaqueRootForGC() .
144 // Maybe should image elements be active DOM nodes?
145 // See https://code.google.com/p/chromium/issues/detail?id=164882
146 if (isHTMLImageElement(*node) && toHTMLImageElement(*node).hasPendin gActivity())
147 return;
148 // FIXME: Remove the special handling for SVG context elements.
149 if (node->isSVGElement() && toSVGElement(node)->isContextElement())
150 return;
151
152 m_nodesInNewSpace.append(node);
153 node->markV8CollectableDuringMinorGC();
154 }
155 }
156
157 void notifyFinished()
158 {
159 Node** nodeIterator = m_nodesInNewSpace.begin();
160 Node** nodeIteratorEnd = m_nodesInNewSpace.end();
161 for (; nodeIterator < nodeIteratorEnd; ++nodeIterator) {
162 Node* node = *nodeIterator;
163 ASSERT(node->containsWrapper());
164 if (node->isV8CollectableDuringMinorGC()) { // This branch is just f or performance.
165 gcTree(m_isolate, node);
166 node->clearV8CollectableDuringMinorGC();
167 }
168 }
169 }
170
171 private:
172 bool traverseTree(Node* rootNode, Vector<Node*, initialNodeVectorSize>* part iallyDependentNodes)
173 {
174 // To make each minor GC time bounded, we might need to give up
175 // traversing at some point for a large DOM tree. That being said,
176 // I could not observe the need even in pathological test cases.
177 for (Node* node = rootNode; node; node = NodeTraversal::next(*node)) {
178 if (node->containsWrapper()) {
179 if (!node->isV8CollectableDuringMinorGC()) {
180 // This node is not in the new space of V8. This indicates t hat
181 // the minor GC cannot anyway judge reachability of this DOM tree.
182 // Thus we give up traversing the DOM tree.
183 return false;
184 }
185 node->clearV8CollectableDuringMinorGC();
186 partiallyDependentNodes->append(node);
187 }
188 if (ShadowRoot* shadowRoot = node->youngestShadowRoot()) {
189 if (!traverseTree(shadowRoot, partiallyDependentNodes))
190 return false;
191 } else if (node->isShadowRoot()) {
192 if (ShadowRoot* shadowRoot = toShadowRoot(node)->olderShadowRoot ()) {
193 if (!traverseTree(shadowRoot, partiallyDependentNodes))
194 return false;
195 }
196 }
197 // <template> has a |content| property holding a DOM fragment which we must traverse,
198 // just like we do for the shadow trees above.
199 if (isHTMLTemplateElement(*node)) {
200 if (!traverseTree(toHTMLTemplateElement(*node).content(), partia llyDependentNodes))
201 return false;
202 }
203
204 // Document maintains the list of imported documents through HTMLImp ortsController.
205 if (node->isDocumentNode()) {
206 Document* document = toDocument(node);
207 HTMLImportsController* controller = document->importsController( );
208 if (controller && document == controller->master()) {
209 for (unsigned i = 0; i < controller->loaderCount(); ++i) {
210 if (!traverseTree(controller->loaderDocumentAt(i), parti allyDependentNodes))
211 return false;
212 }
213 }
214 }
215 }
216 return true;
217 }
218
219 void gcTree(v8::Isolate* isolate, Node* startNode)
220 {
221 Vector<Node*, initialNodeVectorSize> partiallyDependentNodes;
222
223 Node* node = startNode;
224 while (Node* parent = node->parentOrShadowHostOrTemplateHostNode())
225 node = parent;
226
227 if (!traverseTree(node, &partiallyDependentNodes))
228 return;
229
230 // We completed the DOM tree traversal. All wrappers in the DOM tree are
231 // stored in partiallyDependentNodes and are expected to exist in the ne w space of V8.
232 // We report those wrappers to V8 as an object group.
233 Node** nodeIterator = partiallyDependentNodes.begin();
234 Node** const nodeIteratorEnd = partiallyDependentNodes.end();
235 if (nodeIterator == nodeIteratorEnd)
236 return;
237
238 Node* groupRoot = *nodeIterator;
239 for (; nodeIterator != nodeIteratorEnd; ++nodeIterator) {
240 (*nodeIterator)->markAsDependentGroup(groupRoot, isolate);
241 }
242 }
243
244 Vector<Node*> m_nodesInNewSpace;
245 v8::Isolate* m_isolate;
246 };
247
248 class MajorGCWrapperVisitor : public v8::PersistentHandleVisitor {
249 public:
250 explicit MajorGCWrapperVisitor(v8::Isolate* isolate, bool constructRetainedO bjectInfos)
251 : m_isolate(isolate)
252 , m_liveRootGroupIdSet(false)
253 , m_constructRetainedObjectInfos(constructRetainedObjectInfos)
254 {
255 }
256
257 virtual void VisitPersistentHandle(v8::Persistent<v8::Value>* value, uint16_ t classId) OVERRIDE
258 {
259 if (classId != v8DOMNodeClassId && classId != v8DOMObjectClassId)
260 return;
261
262 // Casting to a Handle is safe here, since the Persistent doesn't get GC d
263 // during the GC prologue.
264 ASSERT((*reinterpret_cast<v8::Handle<v8::Value>*>(value))->IsObject());
265 v8::Handle<v8::Object>* wrapper = reinterpret_cast<v8::Handle<v8::Object >*>(value);
266 ASSERT(V8DOMWrapper::isDOMWrapper(*wrapper));
267
268 if (value->IsIndependent())
269 return;
270
271 const WrapperTypeInfo* type = toWrapperTypeInfo(*wrapper);
272 void* object = toNative(*wrapper);
273
274 ActiveDOMObject* activeDOMObject = type->toActiveDOMObject(*wrapper);
275 if (activeDOMObject && activeDOMObject->hasPendingActivity())
276 m_isolate->SetObjectGroupId(*value, liveRootId());
277
278 if (classId == v8DOMNodeClassId) {
279 ASSERT(V8Node::hasInstance(*wrapper, m_isolate));
280 Node* node = static_cast<Node*>(object);
281 if (node->hasEventListeners())
282 addReferencesForNodeWithEventListeners(m_isolate, node, v8::Pers istent<v8::Object>::Cast(*value));
283 Node* root = V8GCController::opaqueRootForGC(node, m_isolate);
284 m_isolate->SetObjectGroupId(*value, v8::UniqueId(reinterpret_cast<in tptr_t>(root)));
285 if (m_constructRetainedObjectInfos)
286 m_groupsWhichNeedRetainerInfo.append(root);
287 } else if (classId == v8DOMObjectClassId) {
288 type->visitDOMWrapper(object, v8::Persistent<v8::Object>::Cast(*valu e), m_isolate);
289 } else {
290 ASSERT_NOT_REACHED();
291 }
292 }
293
294 void notifyFinished()
295 {
296 if (!m_constructRetainedObjectInfos)
297 return;
298 std::sort(m_groupsWhichNeedRetainerInfo.begin(), m_groupsWhichNeedRetain erInfo.end());
299 Node* alreadyAdded = 0;
300 v8::HeapProfiler* profiler = m_isolate->GetHeapProfiler();
301 for (size_t i = 0; i < m_groupsWhichNeedRetainerInfo.size(); ++i) {
302 Node* root = m_groupsWhichNeedRetainerInfo[i];
303 if (root != alreadyAdded) {
304 profiler->SetRetainedObjectInfo(v8::UniqueId(reinterpret_cast<in tptr_t>(root)), new RetainedDOMInfo(root));
305 alreadyAdded = root;
306 }
307 }
308 }
309
310 private:
311 v8::UniqueId liveRootId()
312 {
313 const v8::Persistent<v8::Value>& liveRoot = V8PerIsolateData::from(m_iso late)->ensureLiveRoot();
314 const intptr_t* idPointer = reinterpret_cast<const intptr_t*>(&liveRoot) ;
315 v8::UniqueId id(*idPointer);
316 if (!m_liveRootGroupIdSet) {
317 m_isolate->SetObjectGroupId(liveRoot, id);
318 m_liveRootGroupIdSet = true;
319 }
320 return id;
321 }
322
323 v8::Isolate* m_isolate;
324 Vector<Node*> m_groupsWhichNeedRetainerInfo;
325 bool m_liveRootGroupIdSet;
326 bool m_constructRetainedObjectInfos;
327 };
328
329 static unsigned long long usedHeapSize(v8::Isolate* isolate)
330 {
331 v8::HeapStatistics heapStatistics;
332 isolate->GetHeapStatistics(&heapStatistics);
333 return heapStatistics.used_heap_size();
334 }
335
336 void V8GCController::gcPrologue(v8::GCType type, v8::GCCallbackFlags flags)
337 {
338 // FIXME: It would be nice if the GC callbacks passed the Isolate directly.. ..
339 v8::Isolate* isolate = v8::Isolate::GetCurrent();
340 TRACE_EVENT_BEGIN1(TRACE_DISABLED_BY_DEFAULT("devtools.timeline"), "GCEvent" , "usedHeapSizeBefore", usedHeapSize(isolate));
341 if (type == v8::kGCTypeScavenge)
342 minorGCPrologue(isolate);
343 else if (type == v8::kGCTypeMarkSweepCompact)
344 majorGCPrologue(flags & v8::kGCCallbackFlagConstructRetainedObjectInfos, isolate);
345 }
346
347 void V8GCController::minorGCPrologue(v8::Isolate* isolate)
348 {
349 TRACE_EVENT_BEGIN0("v8", "minorGC");
350 if (isMainThread()) {
351 {
352 TRACE_EVENT_SCOPED_SAMPLING_STATE("blink", "DOMMinorGC");
353 v8::HandleScope scope(isolate);
354 MinorGCWrapperVisitor visitor(isolate);
355 v8::V8::VisitHandlesForPartialDependence(isolate, &visitor);
356 visitor.notifyFinished();
357 }
358 V8PerIsolateData::from(isolate)->setPreviousSamplingState(TRACE_EVENT_GE T_SAMPLING_STATE());
359 TRACE_EVENT_SET_SAMPLING_STATE("v8", "V8MinorGC");
360 }
361 }
362
363 // Create object groups for DOM tree nodes.
364 void V8GCController::majorGCPrologue(bool constructRetainedObjectInfos, v8::Isol ate* isolate)
365 {
366 v8::HandleScope scope(isolate);
367 TRACE_EVENT_BEGIN0("v8", "majorGC");
368 if (isMainThread()) {
369 {
370 TRACE_EVENT_SCOPED_SAMPLING_STATE("blink", "DOMMajorGC");
371 MajorGCWrapperVisitor visitor(isolate, constructRetainedObjectInfos) ;
372 v8::V8::VisitHandlesWithClassIds(&visitor);
373 visitor.notifyFinished();
374 }
375 V8PerIsolateData::from(isolate)->setPreviousSamplingState(TRACE_EVENT_GE T_SAMPLING_STATE());
376 TRACE_EVENT_SET_SAMPLING_STATE("v8", "V8MajorGC");
377 } else {
378 MajorGCWrapperVisitor visitor(isolate, constructRetainedObjectInfos);
379 v8::V8::VisitHandlesWithClassIds(&visitor);
380 visitor.notifyFinished();
381 }
382 }
383
384 void V8GCController::gcEpilogue(v8::GCType type, v8::GCCallbackFlags flags)
385 {
386 // FIXME: It would be nice if the GC callbacks passed the Isolate directly.. ..
387 v8::Isolate* isolate = v8::Isolate::GetCurrent();
388 if (type == v8::kGCTypeScavenge)
389 minorGCEpilogue(isolate);
390 else if (type == v8::kGCTypeMarkSweepCompact)
391 majorGCEpilogue(isolate);
392
393 // Forces a Blink heap garbage collection when a garbage collection
394 // was forced from V8. This is used for tests that force GCs from
395 // JavaScript to verify that objects die when expected.
396 if (flags & v8::kGCCallbackFlagForced) {
397 // This single GC is not enough for two reasons:
398 // (1) The GC is not precise because the GC scans on-stack pointers co nservatively.
399 // (2) One GC is not enough to break a chain of persistent handles. It 's possible that
400 // some heap allocated objects own objects that contain persistent handles
401 // pointing to other heap allocated objects. To break the chain, w e need multiple GCs.
402 //
403 // Regarding (1), we force a precise GC at the end of the current event loop. So if you want
404 // to collect all garbage, you need to wait until the next event loop.
405 // Regarding (2), it would be OK in practice to trigger only one GC per gcEpilogue, because
406 // GCController.collectAll() forces 7 V8's GC.
407 Heap::collectGarbage(ThreadState::HeapPointersOnStack);
408
409 // Forces a precise GC at the end of the current event loop.
410 Heap::setForcePreciseGCForTesting();
411 }
412
413 TRACE_EVENT_END1(TRACE_DISABLED_BY_DEFAULT("devtools.timeline"), "GCEvent", "usedHeapSizeAfter", usedHeapSize(isolate));
414 TRACE_EVENT_INSTANT1(TRACE_DISABLED_BY_DEFAULT("devtools.timeline"), "Update Counters", "data", InspectorUpdateCountersEvent::data());
415 }
416
417 void V8GCController::minorGCEpilogue(v8::Isolate* isolate)
418 {
419 TRACE_EVENT_END0("v8", "minorGC");
420 if (isMainThread())
421 TRACE_EVENT_SET_NONCONST_SAMPLING_STATE(V8PerIsolateData::from(isolate)- >previousSamplingState());
422 }
423
424 void V8GCController::majorGCEpilogue(v8::Isolate* isolate)
425 {
426 v8::HandleScope scope(isolate);
427
428 TRACE_EVENT_END0("v8", "majorGC");
429 if (isMainThread())
430 TRACE_EVENT_SET_NONCONST_SAMPLING_STATE(V8PerIsolateData::from(isolate)- >previousSamplingState());
431 }
432
433 void V8GCController::collectGarbage(v8::Isolate* isolate)
434 {
435 v8::HandleScope handleScope(isolate);
436 RefPtr<ScriptState> scriptState = ScriptState::create(v8::Context::New(isola te), DOMWrapperWorld::create());
437 ScriptState::Scope scope(scriptState.get());
438 V8ScriptRunner::compileAndRunInternalScript(v8String(isolate, "if (gc) gc(); "), isolate);
439 scriptState->disposePerContextData();
440 }
441
442 void V8GCController::reportDOMMemoryUsageToV8(v8::Isolate* isolate)
443 {
444 if (!isMainThread())
445 return;
446
447 static size_t lastUsageReportedToV8 = 0;
448
449 size_t currentUsage = Partitions::currentDOMMemoryUsage();
450 int64_t diff = static_cast<int64_t>(currentUsage) - static_cast<int64_t>(las tUsageReportedToV8);
451 isolate->AdjustAmountOfExternalAllocatedMemory(diff);
452
453 lastUsageReportedToV8 = currentUsage;
454 }
455
456 } // namespace WebCore
OLDNEW
« no previous file with comments | « Source/bindings/v8/V8GCController.h ('k') | Source/bindings/v8/V8GCForContextDispose.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698