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