| 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 |