OLD | NEW |
1 /* | 1 /* |
2 * Copyright (C) 2009 Google Inc. All rights reserved. | 2 * Copyright (C) 2009 Google Inc. All rights reserved. |
3 * | 3 * |
4 * Redistribution and use in source and binary forms, with or without | 4 * Redistribution and use in source and binary forms, with or without |
5 * modification, are permitted provided that the following conditions are | 5 * modification, are permitted provided that the following conditions are |
6 * met: | 6 * met: |
7 * | 7 * |
8 * * Redistributions of source code must retain the above copyright | 8 * * Redistributions of source code must retain the above copyright |
9 * notice, this list of conditions and the following disclaimer. | 9 * notice, this list of conditions and the following disclaimer. |
10 * * Redistributions in binary form must reproduce the above | 10 * * Redistributions in binary form must reproduce the above |
(...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
49 #include "public/platform/BlameContext.h" | 49 #include "public/platform/BlameContext.h" |
50 #include "public/platform/Platform.h" | 50 #include "public/platform/Platform.h" |
51 #include "wtf/Vector.h" | 51 #include "wtf/Vector.h" |
52 #include "wtf/allocator/Partitions.h" | 52 #include "wtf/allocator/Partitions.h" |
53 #include <algorithm> | 53 #include <algorithm> |
54 #include <unordered_map> | 54 #include <unordered_map> |
55 #include <unordered_set> | 55 #include <unordered_set> |
56 | 56 |
57 namespace blink { | 57 namespace blink { |
58 | 58 |
59 // FIXME: This should use opaque GC roots. | |
60 static void addReferencesForNodeWithEventListeners( | |
61 v8::Isolate* isolate, | |
62 Node* node, | |
63 const v8::Persistent<v8::Object>& wrapper) { | |
64 ASSERT(node->hasEventListeners()); | |
65 | |
66 EventListenerIterator iterator(node); | |
67 while (EventListener* listener = iterator.nextListener()) { | |
68 if (listener->type() != EventListener::JSEventListenerType) | |
69 continue; | |
70 V8AbstractEventListener* v8listener = | |
71 static_cast<V8AbstractEventListener*>(listener); | |
72 if (!v8listener->hasExistingListenerObject()) | |
73 continue; | |
74 | |
75 isolate->SetReference( | |
76 wrapper, v8::Persistent<v8::Value>::Cast( | |
77 v8listener->existingListenerObjectPersistentHandle())); | |
78 } | |
79 } | |
80 | |
81 Node* V8GCController::opaqueRootForGC(v8::Isolate*, Node* node) { | 59 Node* V8GCController::opaqueRootForGC(v8::Isolate*, Node* node) { |
82 ASSERT(node); | 60 ASSERT(node); |
83 if (node->isConnected()) { | 61 if (node->isConnected()) { |
84 Document& document = node->document(); | 62 Document& document = node->document(); |
85 if (HTMLImportsController* controller = document.importsController()) | 63 if (HTMLImportsController* controller = document.importsController()) |
86 return controller->master(); | 64 return controller->master(); |
87 return &document; | 65 return &document; |
88 } | 66 } |
89 | 67 |
90 if (node->isAttributeNode()) { | 68 if (node->isAttributeNode()) { |
(...skipping 194 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
285 new HeapSnaphotWrapperVisitor(isolate))); | 263 new HeapSnaphotWrapperVisitor(isolate))); |
286 | 264 |
287 HeapSnaphotWrapperVisitor* tracer = | 265 HeapSnaphotWrapperVisitor* tracer = |
288 reinterpret_cast<HeapSnaphotWrapperVisitor*>(scope.currentVisitor()); | 266 reinterpret_cast<HeapSnaphotWrapperVisitor*>(scope.currentVisitor()); |
289 tracer->collectV8Roots(); | 267 tracer->collectV8Roots(); |
290 tracer->traceV8Roots(); | 268 tracer->traceV8Roots(); |
291 tracer->tracePendingActivities(); | 269 tracer->tracePendingActivities(); |
292 return v8::HeapProfiler::RetainerInfos{tracer->groups(), tracer->edges()}; | 270 return v8::HeapProfiler::RetainerInfos{tracer->groups(), tracer->edges()}; |
293 } | 271 } |
294 | 272 |
295 class MajorGCWrapperVisitor : public v8::PersistentHandleVisitor { | |
296 public: | |
297 explicit MajorGCWrapperVisitor(v8::Isolate* isolate, | |
298 bool constructRetainedObjectInfos) | |
299 : m_isolate(isolate), | |
300 m_domObjectsWithPendingActivity(0), | |
301 m_liveRootGroupIdSet(false), | |
302 m_constructRetainedObjectInfos(constructRetainedObjectInfos) {} | |
303 | |
304 void VisitPersistentHandle(v8::Persistent<v8::Value>* value, | |
305 uint16_t classId) override { | |
306 if (classId != WrapperTypeInfo::NodeClassId && | |
307 classId != WrapperTypeInfo::ObjectClassId) | |
308 return; | |
309 | |
310 if (value->IsIndependent()) | |
311 return; | |
312 | |
313 v8::Local<v8::Object> wrapper = v8::Local<v8::Object>::New( | |
314 m_isolate, v8::Persistent<v8::Object>::Cast(*value)); | |
315 DCHECK(V8DOMWrapper::hasInternalFieldsSet(wrapper)); | |
316 | |
317 const WrapperTypeInfo* type = toWrapperTypeInfo(wrapper); | |
318 if (type->isActiveScriptWrappable() && | |
319 toScriptWrappable(wrapper)->hasPendingActivity()) { | |
320 // Enable hasPendingActivity only when the associated | |
321 // ExecutionContext is not yet detached. This is a work-around | |
322 // to avoid memory leaks caused by hasPendingActivity that keeps | |
323 // returning true forever. This will be okay in practice because | |
324 // the spec requires to stop almost all DOM activities when the | |
325 // associated browsing context is detached. However, the real | |
326 // problem is that some hasPendingActivity's are wrongly implemented | |
327 // and never return false. | |
328 // TODO(haraken): Implement correct lifetime using traceWrapper. | |
329 ExecutionContext* context = | |
330 toExecutionContext(wrapper->CreationContext()); | |
331 if (context && !context->isContextDestroyed()) { | |
332 m_isolate->SetObjectGroupId(*value, liveRootId()); | |
333 ++m_domObjectsWithPendingActivity; | |
334 } | |
335 } | |
336 | |
337 if (classId == WrapperTypeInfo::NodeClassId) { | |
338 DCHECK(V8Node::hasInstance(wrapper, m_isolate)); | |
339 Node* node = V8Node::toImpl(wrapper); | |
340 if (node->hasEventListeners()) | |
341 addReferencesForNodeWithEventListeners( | |
342 m_isolate, node, v8::Persistent<v8::Object>::Cast(*value)); | |
343 Node* root = V8GCController::opaqueRootForGC(m_isolate, node); | |
344 m_isolate->SetObjectGroupId( | |
345 *value, v8::UniqueId(reinterpret_cast<intptr_t>(root))); | |
346 if (m_constructRetainedObjectInfos) | |
347 m_groupsWhichNeedRetainerInfo.push_back(root); | |
348 } else if (classId == WrapperTypeInfo::ObjectClassId) { | |
349 if (!RuntimeEnabledFeatures::traceWrappablesEnabled()) { | |
350 type->visitDOMWrapper(m_isolate, toScriptWrappable(wrapper), | |
351 v8::Persistent<v8::Object>::Cast(*value)); | |
352 } | |
353 } else { | |
354 NOTREACHED(); | |
355 } | |
356 } | |
357 | |
358 void notifyFinished() { | |
359 if (!m_constructRetainedObjectInfos) | |
360 return; | |
361 std::sort(m_groupsWhichNeedRetainerInfo.begin(), | |
362 m_groupsWhichNeedRetainerInfo.end()); | |
363 Node* alreadyAdded = 0; | |
364 v8::HeapProfiler* profiler = m_isolate->GetHeapProfiler(); | |
365 for (size_t i = 0; i < m_groupsWhichNeedRetainerInfo.size(); ++i) { | |
366 Node* root = m_groupsWhichNeedRetainerInfo[i]; | |
367 if (root != alreadyAdded) { | |
368 profiler->SetRetainedObjectInfo( | |
369 v8::UniqueId(reinterpret_cast<intptr_t>(root)), | |
370 new RetainedDOMInfo(root)); | |
371 alreadyAdded = root; | |
372 } | |
373 } | |
374 if (m_liveRootGroupIdSet) { | |
375 profiler->SetRetainedObjectInfo( | |
376 liveRootId(), | |
377 new SuspendableObjectsInfo(m_domObjectsWithPendingActivity)); | |
378 } | |
379 } | |
380 | |
381 private: | |
382 v8::UniqueId liveRootId() { | |
383 const v8::Persistent<v8::Value>& liveRoot = | |
384 V8PerIsolateData::from(m_isolate)->ensureLiveRoot(); | |
385 const intptr_t* idPointer = reinterpret_cast<const intptr_t*>(&liveRoot); | |
386 v8::UniqueId id(*idPointer); | |
387 if (!m_liveRootGroupIdSet) { | |
388 m_isolate->SetObjectGroupId(liveRoot, id); | |
389 m_liveRootGroupIdSet = true; | |
390 ++m_domObjectsWithPendingActivity; | |
391 } | |
392 return id; | |
393 } | |
394 | |
395 v8::Isolate* m_isolate; | |
396 // v8 guarantees that Blink will not regain control while a v8 GC runs | |
397 // (=> no Oilpan GCs will be triggered), hence raw, untraced members | |
398 // can safely be kept here. | |
399 Vector<UntracedMember<Node>> m_groupsWhichNeedRetainerInfo; | |
400 int m_domObjectsWithPendingActivity; | |
401 bool m_liveRootGroupIdSet; | |
402 bool m_constructRetainedObjectInfos; | |
403 }; | |
404 | |
405 static unsigned long long usedHeapSize(v8::Isolate* isolate) { | 273 static unsigned long long usedHeapSize(v8::Isolate* isolate) { |
406 v8::HeapStatistics heapStatistics; | 274 v8::HeapStatistics heapStatistics; |
407 isolate->GetHeapStatistics(&heapStatistics); | 275 isolate->GetHeapStatistics(&heapStatistics); |
408 return heapStatistics.used_heap_size(); | 276 return heapStatistics.used_heap_size(); |
409 } | 277 } |
410 | 278 |
411 namespace { | 279 namespace { |
412 | 280 |
413 void visitWeakHandlesForMinorGC(v8::Isolate* isolate) { | 281 void visitWeakHandlesForMinorGC(v8::Isolate* isolate) { |
414 MinorGCUnmodifiedWrapperVisitor visitor(isolate); | 282 MinorGCUnmodifiedWrapperVisitor visitor(isolate); |
415 isolate->VisitWeakHandles(&visitor); | 283 isolate->VisitWeakHandles(&visitor); |
416 } | 284 } |
417 | 285 |
418 void objectGroupingForMajorGC(v8::Isolate* isolate, | |
419 bool constructRetainedObjectInfos) { | |
420 MajorGCWrapperVisitor visitor(isolate, constructRetainedObjectInfos); | |
421 isolate->VisitHandlesWithClassIds(&visitor); | |
422 visitor.notifyFinished(); | |
423 } | |
424 | |
425 void gcPrologueForMajorGC(v8::Isolate* isolate, | |
426 bool constructRetainedObjectInfos) { | |
427 if (!RuntimeEnabledFeatures::traceWrappablesEnabled() || | |
428 constructRetainedObjectInfos) { | |
429 objectGroupingForMajorGC(isolate, constructRetainedObjectInfos); | |
430 } | |
431 } | |
432 | |
433 } // namespace | 286 } // namespace |
434 | 287 |
435 void V8GCController::gcPrologue(v8::Isolate* isolate, | 288 void V8GCController::gcPrologue(v8::Isolate* isolate, |
436 v8::GCType type, | 289 v8::GCType type, |
437 v8::GCCallbackFlags flags) { | 290 v8::GCCallbackFlags flags) { |
438 if (isMainThread()) | 291 if (isMainThread()) |
439 ScriptForbiddenScope::enter(); | 292 ScriptForbiddenScope::enter(); |
440 | 293 |
441 // Attribute garbage collection to the all frames instead of a specific | 294 // Attribute garbage collection to the all frames instead of a specific |
442 // frame. | 295 // frame. |
(...skipping 16 matching lines...) Expand all Loading... |
459 "usedHeapSizeBefore", usedHeapSize(isolate)); | 312 "usedHeapSizeBefore", usedHeapSize(isolate)); |
460 visitWeakHandlesForMinorGC(isolate); | 313 visitWeakHandlesForMinorGC(isolate); |
461 break; | 314 break; |
462 case v8::kGCTypeMarkSweepCompact: | 315 case v8::kGCTypeMarkSweepCompact: |
463 if (ThreadState::current()) | 316 if (ThreadState::current()) |
464 ThreadState::current()->willStartV8GC(BlinkGC::V8MajorGC); | 317 ThreadState::current()->willStartV8GC(BlinkGC::V8MajorGC); |
465 | 318 |
466 TRACE_EVENT_BEGIN2("devtools.timeline,v8", "MajorGC", | 319 TRACE_EVENT_BEGIN2("devtools.timeline,v8", "MajorGC", |
467 "usedHeapSizeBefore", usedHeapSize(isolate), "type", | 320 "usedHeapSizeBefore", usedHeapSize(isolate), "type", |
468 "atomic pause"); | 321 "atomic pause"); |
469 gcPrologueForMajorGC( | |
470 isolate, flags & v8::kGCCallbackFlagConstructRetainedObjectInfos); | |
471 break; | 322 break; |
472 case v8::kGCTypeIncrementalMarking: | 323 case v8::kGCTypeIncrementalMarking: |
473 if (ThreadState::current()) | 324 if (ThreadState::current()) |
474 ThreadState::current()->willStartV8GC(BlinkGC::V8MajorGC); | 325 ThreadState::current()->willStartV8GC(BlinkGC::V8MajorGC); |
475 | 326 |
476 TRACE_EVENT_BEGIN2("devtools.timeline,v8", "MajorGC", | 327 TRACE_EVENT_BEGIN2("devtools.timeline,v8", "MajorGC", |
477 "usedHeapSizeBefore", usedHeapSize(isolate), "type", | 328 "usedHeapSizeBefore", usedHeapSize(isolate), "type", |
478 "incremental marking"); | 329 "incremental marking"); |
479 gcPrologueForMajorGC( | |
480 isolate, flags & v8::kGCCallbackFlagConstructRetainedObjectInfos); | |
481 break; | 330 break; |
482 case v8::kGCTypeProcessWeakCallbacks: | 331 case v8::kGCTypeProcessWeakCallbacks: |
483 TRACE_EVENT_BEGIN2("devtools.timeline,v8", "MajorGC", | 332 TRACE_EVENT_BEGIN2("devtools.timeline,v8", "MajorGC", |
484 "usedHeapSizeBefore", usedHeapSize(isolate), "type", | 333 "usedHeapSizeBefore", usedHeapSize(isolate), "type", |
485 "weak processing"); | 334 "weak processing"); |
486 break; | 335 break; |
487 default: | 336 default: |
488 ASSERT_NOT_REACHED(); | 337 ASSERT_NOT_REACHED(); |
489 } | 338 } |
490 } | 339 } |
(...skipping 197 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
688 double startTime = WTF::currentTimeMS(); | 537 double startTime = WTF::currentTimeMS(); |
689 v8::HandleScope scope(isolate); | 538 v8::HandleScope scope(isolate); |
690 PendingActivityVisitor visitor(isolate, executionContext); | 539 PendingActivityVisitor visitor(isolate, executionContext); |
691 toIsolate(executionContext)->VisitHandlesWithClassIds(&visitor); | 540 toIsolate(executionContext)->VisitHandlesWithClassIds(&visitor); |
692 scanPendingActivityHistogram.count( | 541 scanPendingActivityHistogram.count( |
693 static_cast<int>(WTF::currentTimeMS() - startTime)); | 542 static_cast<int>(WTF::currentTimeMS() - startTime)); |
694 return visitor.pendingActivityFound(); | 543 return visitor.pendingActivityFound(); |
695 } | 544 } |
696 | 545 |
697 } // namespace blink | 546 } // namespace blink |
OLD | NEW |