OLD | NEW |
1 /* | 1 /* |
2 * Copyright (C) 2013 Google Inc. All rights reserved. | 2 * Copyright (C) 2013 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 514 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
525 return mutex; | 525 return mutex; |
526 } | 526 } |
527 | 527 |
528 // TODO(haraken): We should improve the GC heuristics. | 528 // TODO(haraken): We should improve the GC heuristics. |
529 // These heuristics affect performance significantly. | 529 // These heuristics affect performance significantly. |
530 bool ThreadState::shouldScheduleIdleGC() | 530 bool ThreadState::shouldScheduleIdleGC() |
531 { | 531 { |
532 if (gcState() != NoGCScheduled) | 532 if (gcState() != NoGCScheduled) |
533 return false; | 533 return false; |
534 #if ENABLE(IDLE_GC) | 534 #if ENABLE(IDLE_GC) |
| 535 // Avoid potential overflow by truncating to Kb. |
| 536 size_t allocatedObjectSizeKb = Heap::allocatedObjectSize() >> 10; |
535 // The estimated size is updated when the main thread finishes lazy | 537 // The estimated size is updated when the main thread finishes lazy |
536 // sweeping. If this thread reaches here before the main thread finishes | 538 // sweeping. If this thread reaches here before the main thread finishes |
537 // lazy sweeping, the thread will use the estimated size of the last GC. | 539 // lazy sweeping, the thread will use the estimated size of the last GC. |
538 size_t estimatedLiveObjectSize = Heap::estimatedLiveObjectSize(); | 540 size_t estimatedLiveObjectSizeKb = Heap::estimatedLiveObjectSize() >> 10; |
539 size_t allocatedObjectSize = Heap::allocatedObjectSize(); | |
540 // Heap::markedObjectSize() may be underestimated if any thread has not | 541 // Heap::markedObjectSize() may be underestimated if any thread has not |
541 // finished completeSweep(). | 542 // finished completeSweep(). |
542 size_t currentObjectSize = allocatedObjectSize + Heap::markedObjectSize() +
WTF::Partitions::totalSizeOfCommittedPages(); | 543 size_t currentObjectSizeKb = allocatedObjectSizeKb + ((Heap::markedObjectSiz
e() + WTF::Partitions::totalSizeOfCommittedPages()) >> 10); |
543 // Schedule an idle GC if Oilpan has allocated more than 1 MB since | 544 // Schedule an idle GC if Oilpan has allocated more than 1 MB since |
544 // the last GC and the current memory usage is >50% larger than | 545 // the last GC and the current memory usage is >50% larger than |
545 // the estimated live memory usage. | 546 // the estimated live memory usage. |
546 return allocatedObjectSize >= 1024 * 1024 && currentObjectSize > estimatedLi
veObjectSize * 3 / 2; | 547 return allocatedObjectSizeKb >= 1024 && currentObjectSizeKb > estimatedLiveO
bjectSizeKb * 3 / 2; |
547 #else | 548 #else |
548 return false; | 549 return false; |
549 #endif | 550 #endif |
550 } | 551 } |
551 | 552 |
552 // TODO(haraken): We should improve the GC heuristics. | 553 // TODO(haraken): We should improve the GC heuristics. |
553 // These heuristics affect performance significantly. | 554 // These heuristics affect performance significantly. |
554 bool ThreadState::shouldSchedulePreciseGC() | 555 bool ThreadState::shouldSchedulePreciseGC() |
555 { | 556 { |
556 if (gcState() != NoGCScheduled) | 557 if (gcState() != NoGCScheduled) |
557 return false; | 558 return false; |
558 #if ENABLE(IDLE_GC) | 559 #if ENABLE(IDLE_GC) |
559 return false; | 560 return false; |
560 #else | 561 #else |
| 562 // Avoid potential overflow by truncating to Kb. |
| 563 size_t allocatedObjectSizeKb = Heap::allocatedObjectSize() >> 10; |
561 // The estimated size is updated when the main thread finishes lazy | 564 // The estimated size is updated when the main thread finishes lazy |
562 // sweeping. If this thread reaches here before the main thread finishes | 565 // sweeping. If this thread reaches here before the main thread finishes |
563 // lazy sweeping, the thread will use the estimated size of the last GC. | 566 // lazy sweeping, the thread will use the estimated size of the last GC. |
564 size_t estimatedLiveObjectSize = Heap::estimatedLiveObjectSize(); | 567 size_t estimatedLiveObjectSizeKb = Heap::estimatedLiveObjectSize() >> 10; |
565 size_t allocatedObjectSize = Heap::allocatedObjectSize(); | |
566 // Heap::markedObjectSize() may be underestimated if any thread has not | 568 // Heap::markedObjectSize() may be underestimated if any thread has not |
567 // finished completeSweep(). | 569 // finished completeSweep(). |
568 size_t currentObjectSize = allocatedObjectSize + Heap::markedObjectSize() +
WTF::Partitions::totalSizeOfCommittedPages(); | 570 size_t currentObjectSizeKb = allocatedObjectSizeKb + ((Heap::markedObjectSiz
e() + WTF::Partitions::totalSizeOfCommittedPages()) >> 10); |
569 // Schedule a precise GC if Oilpan has allocated more than 1 MB since | 571 // Schedule a precise GC if Oilpan has allocated more than 1 MB since |
570 // the last GC and the current memory usage is >50% larger than | 572 // the last GC and the current memory usage is >50% larger than |
571 // the estimated live memory usage. | 573 // the estimated live memory usage. |
572 return allocatedObjectSize >= 1024 * 1024 && currentObjectSize > estimatedLi
veObjectSize * 3 / 2; | 574 return allocatedObjectSizeKb >= 1024 && currentObjectSizeKb > estimatedLiveO
bjectSizeKb * 3 / 2; |
573 #endif | 575 #endif |
574 } | 576 } |
575 | 577 |
| 578 bool ThreadState::shouldForceMemoryPressureGC() |
| 579 { |
| 580 // Avoid potential overflow by truncating to Kb. |
| 581 size_t currentObjectSizeKb = (Heap::allocatedObjectSize() + Heap::markedObje
ctSize() + WTF::Partitions::totalSizeOfCommittedPages()) >> 10; |
| 582 size_t estimatedLiveObjectSizeKb = (Heap::estimatedLiveObjectSize()) >> 10; |
| 583 if (currentObjectSizeKb < 300 * 1024) |
| 584 return false; |
| 585 |
| 586 // If we're consuming too much memory, trigger a conservative GC |
| 587 // aggressively. This is a safe guard to avoid OOM. |
| 588 return currentObjectSizeKb > (estimatedLiveObjectSizeKb * 3) / 2; |
| 589 } |
| 590 |
576 // TODO(haraken): We should improve the GC heuristics. | 591 // TODO(haraken): We should improve the GC heuristics. |
577 // These heuristics affect performance significantly. | 592 // These heuristics affect performance significantly. |
578 bool ThreadState::shouldForceConservativeGC() | 593 bool ThreadState::shouldForceConservativeGC() |
579 { | 594 { |
580 if (UNLIKELY(isGCForbidden())) | 595 if (UNLIKELY(isGCForbidden())) |
581 return false; | 596 return false; |
582 | 597 |
| 598 if (shouldForceMemoryPressureGC()) |
| 599 return true; |
| 600 |
| 601 // Avoid potential overflow by truncating to Kb. |
| 602 size_t allocatedObjectSizeKb = Heap::allocatedObjectSize() >> 10; |
583 // The estimated size is updated when the main thread finishes lazy | 603 // The estimated size is updated when the main thread finishes lazy |
584 // sweeping. If this thread reaches here before the main thread finishes | 604 // sweeping. If this thread reaches here before the main thread finishes |
585 // lazy sweeping, the thread will use the estimated size of the last GC. | 605 // lazy sweeping, the thread will use the estimated size of the last GC. |
586 size_t estimatedLiveObjectSize = Heap::estimatedLiveObjectSize(); | 606 size_t estimatedLiveObjectSizeKb = Heap::estimatedLiveObjectSize() >> 10; |
587 size_t allocatedObjectSize = Heap::allocatedObjectSize(); | |
588 // Heap::markedObjectSize() may be underestimated if any thread has not | 607 // Heap::markedObjectSize() may be underestimated if any thread has not |
589 // finished completeSweep(). | 608 // finished completeSweep(). |
590 size_t currentObjectSize = allocatedObjectSize + Heap::markedObjectSize() +
WTF::Partitions::totalSizeOfCommittedPages(); | 609 size_t currentObjectSizeKb = allocatedObjectSizeKb + ((Heap::markedObjectSiz
e() + WTF::Partitions::totalSizeOfCommittedPages()) >> 10); |
591 if (currentObjectSize >= 300 * 1024 * 1024) { | 610 |
592 // If we're consuming too much memory, trigger a conservative GC | |
593 // aggressively. This is a safe guard to avoid OOM. | |
594 return currentObjectSize > estimatedLiveObjectSize * 3 / 2; | |
595 } | |
596 // Schedule a conservative GC if Oilpan has allocated more than 32 MB since | 611 // Schedule a conservative GC if Oilpan has allocated more than 32 MB since |
597 // the last GC and the current memory usage is >400% larger than | 612 // the last GC and the current memory usage is >400% larger than |
598 // the estimated live memory usage. | 613 // the estimated live memory usage. |
599 // TODO(haraken): 400% is too large. Lower the heap growing factor. | 614 // TODO(haraken): 400% is too large. Lower the heap growing factor. |
600 return allocatedObjectSize >= 32 * 1024 * 1024 && currentObjectSize > 5 * es
timatedLiveObjectSize; | 615 return allocatedObjectSizeKb >= 32 * 1024 && currentObjectSizeKb > 5 * estim
atedLiveObjectSizeKb; |
601 } | 616 } |
602 | 617 |
603 void ThreadState::scheduleGCIfNeeded() | 618 void ThreadState::scheduleGCIfNeeded() |
604 { | 619 { |
605 checkThread(); | 620 checkThread(); |
606 // Allocation is allowed during sweeping, but those allocations should not | 621 // Allocation is allowed during sweeping, but those allocations should not |
607 // trigger nested GCs. | 622 // trigger nested GCs. |
608 if (isSweepingInProgress()) | 623 if (isSweepingInProgress()) |
609 return; | 624 return; |
610 ASSERT(!sweepForbidden()); | 625 ASSERT(!sweepForbidden()); |
(...skipping 171 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
782 m_gcState = gcState; | 797 m_gcState = gcState; |
783 } | 798 } |
784 | 799 |
785 #undef VERIFY_STATE_TRANSITION | 800 #undef VERIFY_STATE_TRANSITION |
786 | 801 |
787 ThreadState::GCState ThreadState::gcState() const | 802 ThreadState::GCState ThreadState::gcState() const |
788 { | 803 { |
789 return m_gcState; | 804 return m_gcState; |
790 } | 805 } |
791 | 806 |
792 void ThreadState::didV8GC() | 807 void ThreadState::didV8MajorGC(bool forceGC) |
793 { | 808 { |
794 checkThread(); | 809 checkThread(); |
795 if (isMainThread()) { | 810 if (isMainThread()) { |
796 // Lower the estimated live object size because the V8 major GC is | 811 // Lower the estimated live object size because the V8 major GC is |
797 // expected to have collected a lot of DOM wrappers and dropped | 812 // expected to have collected a lot of DOM wrappers and dropped |
798 // references to their DOM objects. | 813 // references to their DOM objects. |
799 Heap::setEstimatedLiveObjectSize(Heap::estimatedLiveObjectSize() / 2); | 814 Heap::setEstimatedLiveObjectSize(Heap::estimatedLiveObjectSize() / 2); |
| 815 |
| 816 if (forceGC) { |
| 817 // This single GC is not enough for two reasons: |
| 818 // (1) The GC is not precise because the GC scans on-stack pointer
s conservatively. |
| 819 // (2) One GC is not enough to break a chain of persistent handles
. It's possible that |
| 820 // some heap allocated objects own objects that contain persis
tent handles |
| 821 // pointing to other heap allocated objects. To break the chai
n, we need multiple GCs. |
| 822 // |
| 823 // Regarding (1), we force a precise GC at the end of the current ev
ent loop. So if you want |
| 824 // to collect all garbage, you need to wait until the next event loo
p. |
| 825 // Regarding (2), it would be OK in practice to trigger only one GC
per gcEpilogue, because |
| 826 // GCController.collectAll() forces 7 V8's GC. |
| 827 Heap::collectGarbage(ThreadState::HeapPointersOnStack, ThreadState::
GCWithSweep, Heap::ForcedGC); |
| 828 |
| 829 // Forces a precise GC at the end of the current event loop. |
| 830 ThreadState::current()->setGCState(ThreadState::FullGCScheduled); |
| 831 return; |
| 832 } |
| 833 |
| 834 // If under memory pressure, complete sweeping before initiating |
| 835 // the urgent conservative GC. |
| 836 if (shouldForceMemoryPressureGC()) |
| 837 completeSweep(); |
| 838 |
| 839 // Schedule an Oilpan GC to avoid the following scenario: |
| 840 // (1) A DOM object X holds a v8::Persistent to a V8 object. |
| 841 // Assume that X is small but the V8 object is huge. |
| 842 // The v8::Persistent is released when X is destructed. |
| 843 // (2) X's DOM wrapper is created. |
| 844 // (3) The DOM wrapper becomes unreachable. |
| 845 // (4) V8 triggers a GC. The V8's GC collects the DOM wrapper. |
| 846 // However, X is not collected until a next Oilpan's GC is |
| 847 // triggered. |
| 848 // (5) If a lot of such DOM objects are created, we end up with |
| 849 // a situation where V8's GC collects the DOM wrappers but |
| 850 // the DOM objects are not collected forever. (Note that |
| 851 // Oilpan's GC is not triggered unless Oilpan's heap gets full.) |
| 852 // (6) V8 hits OOM. |
| 853 scheduleGCIfNeeded(); |
800 } | 854 } |
801 } | 855 } |
802 | 856 |
803 void ThreadState::runScheduledGC(StackState stackState) | 857 void ThreadState::runScheduledGC(StackState stackState) |
804 { | 858 { |
805 checkThread(); | 859 checkThread(); |
806 if (stackState != NoHeapPointersOnStack) | 860 if (stackState != NoHeapPointersOnStack) |
807 return; | 861 return; |
808 | 862 |
809 // If a safe point is entered while initiating a GC, we clearly do | 863 // If a safe point is entered while initiating a GC, we clearly do |
(...skipping 597 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1407 json->beginArray(it->key.ascii().data()); | 1461 json->beginArray(it->key.ascii().data()); |
1408 for (size_t age = 0; age <= maxHeapObjectAge; ++age) | 1462 for (size_t age = 0; age <= maxHeapObjectAge; ++age) |
1409 json->pushInteger(it->value.ages[age]); | 1463 json->pushInteger(it->value.ages[age]); |
1410 json->endArray(); | 1464 json->endArray(); |
1411 } | 1465 } |
1412 TRACE_EVENT_OBJECT_SNAPSHOT_WITH_ID(TRACE_DISABLED_BY_DEFAULT("blink_gc"), s
tatsName, this, json.release()); | 1466 TRACE_EVENT_OBJECT_SNAPSHOT_WITH_ID(TRACE_DISABLED_BY_DEFAULT("blink_gc"), s
tatsName, this, json.release()); |
1413 } | 1467 } |
1414 #endif | 1468 #endif |
1415 | 1469 |
1416 } // namespace blink | 1470 } // namespace blink |
OLD | NEW |