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 = allocatedObjectSize + ((Heap::markedObjectSize( ) + 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(); | |
haraken
2015/06/11 08:49:17
Can we move the completeSweep() into shouldForceMe
sof
2015/06/11 08:56:14
It could be done; the alternative is to allow cons
| |
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 |