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 551 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
562 { | 562 { |
563 if (m_persistentAllocated >= m_persistentFreed) | 563 if (m_persistentAllocated >= m_persistentFreed) |
564 Heap::increasePersistentCount(m_persistentAllocated - m_persistentFreed)
; | 564 Heap::increasePersistentCount(m_persistentAllocated - m_persistentFreed)
; |
565 else | 565 else |
566 Heap::decreasePersistentCount(m_persistentFreed - m_persistentAllocated)
; | 566 Heap::decreasePersistentCount(m_persistentFreed - m_persistentAllocated)
; |
567 Heap::increaseCollectedPersistentCount(m_persistentFreed); | 567 Heap::increaseCollectedPersistentCount(m_persistentFreed); |
568 m_persistentAllocated = 0; | 568 m_persistentAllocated = 0; |
569 m_persistentFreed = 0; | 569 m_persistentFreed = 0; |
570 } | 570 } |
571 | 571 |
| 572 size_t ThreadState::currentObjectSize() |
| 573 { |
| 574 return Heap::allocatedObjectSize() + Heap::markedObjectSize() + WTF::Partiti
ons::totalSizeOfCommittedPages(); |
| 575 } |
| 576 |
572 size_t ThreadState::estimatedLiveObjectSize() | 577 size_t ThreadState::estimatedLiveObjectSize() |
573 { | 578 { |
574 // We estimate the live object size with the following equations. | 579 // We estimate the live object size with the following equations. |
575 // | 580 // |
576 // heapSizePerPersistent = (marked(t0, t1) + partitionAlloc(t0)) / persist
entCount(t0) | 581 // heapSizePerPersistent = (marked(t0, t1) + partitionAlloc(t0)) / persist
entCount(t0) |
577 // estimatedLiveObjectSize = marked(t0, t) + allocated(t0, t) + partitionA
lloc(t) - heapSizePerPersistent * collectedPersistentCount(t0, t) | 582 // estimatedLiveObjectSize = marked(t0, t) + allocated(t0, t) + partitionA
lloc(t) - heapSizePerPersistent * collectedPersistentCount(t0, t) |
578 // | 583 // |
579 // t0: The time when the last collectGarbage runs. | 584 // t0: The time when the last collectGarbage runs. |
580 // t1: The time when the last completeSweep runs. | 585 // t1: The time when the last completeSweep runs. |
581 // t: The current time. | 586 // t: The current time. |
582 // marked(t0, t): The size of marked objects between t0 and t. | 587 // marked(t0, t): The size of marked objects between t0 and t. |
583 // allocated(t0, t): The size of newly allocated objects between t0 and t. | 588 // allocated(t0, t): The size of newly allocated objects between t0 and t. |
584 // persistentCount(t): The number of existing persistent handles at t. | 589 // persistentCount(t): The number of existing persistent handles at t. |
585 // collectedPersistentCount(t0, t): | 590 // collectedPersistentCount(t0, t): |
586 // The number of persistent handles collected between | 591 // The number of persistent handles collected between |
587 // t0 and t. | 592 // t0 and t. |
588 // partitionAlloc(t): The size of allocated memory in PartitionAlloc at t. | 593 // partitionAlloc(t): The size of allocated memory in PartitionAlloc at t. |
589 size_t currentHeapSize = currentObjectSize(); | 594 size_t currentHeapSize = currentObjectSize(); |
590 size_t heapSizeRetainedByCollectedPersistents = Heap::heapSizePerPersistent(
) * Heap::collectedPersistentCount(); | 595 size_t heapSizeRetainedByCollectedPersistents = Heap::heapSizePerPersistent(
) * Heap::collectedPersistentCount(); |
591 size_t estimatedSize = 0; | 596 size_t estimatedSize = 0; |
592 if (currentHeapSize > heapSizeRetainedByCollectedPersistents) | 597 if (currentHeapSize > heapSizeRetainedByCollectedPersistents) |
593 estimatedSize = currentHeapSize - heapSizeRetainedByCollectedPersistents
; | 598 estimatedSize = currentHeapSize - heapSizeRetainedByCollectedPersistents
; |
594 TRACE_COUNTER1("blink_gc", "ThreadState::currentHeapSizeKB", std::min(curren
tHeapSize / 1024, static_cast<size_t>(INT_MAX))); | |
595 TRACE_COUNTER1("blink_gc", "ThreadState::estimatedLiveObjectSizeKB", std::mi
n(estimatedSize / 1024, static_cast<size_t>(INT_MAX))); | |
596 TRACE_COUNTER1("blink_gc", "ThreadState::heapGrowingRate", static_cast<int>(
100.0 * currentHeapSize / estimatedSize)); | |
597 return estimatedSize; | 599 return estimatedSize; |
598 } | 600 } |
599 | 601 |
600 size_t ThreadState::currentObjectSize() | 602 double ThreadState::heapGrowingRate() |
601 { | 603 { |
602 return Heap::allocatedObjectSize() + Heap::markedObjectSize() + WTF::Partiti
ons::totalSizeOfCommittedPages(); | 604 size_t currentSize = currentObjectSize(); |
| 605 size_t estimatedSize = estimatedLiveObjectSize(); |
| 606 double growingRate = 1.0 * currentSize / estimatedSize; |
| 607 TRACE_COUNTER1("blink_gc", "ThreadState::currentHeapSizeKB", std::min(curren
tSize / 1024, static_cast<size_t>(INT_MAX))); |
| 608 TRACE_COUNTER1("blink_gc", "ThreadState::estimatedLiveObjectSizeKB", std::mi
n(estimatedSize / 1024, static_cast<size_t>(INT_MAX))); |
| 609 TRACE_COUNTER1("blink_gc", "ThreadState::heapGrowingRate", static_cast<int>(
100 * growingRate)); |
| 610 return growingRate; |
| 611 } |
| 612 |
| 613 // TODO(haraken): We should improve the GC heuristics. |
| 614 // The heuristics affect performance significantly. |
| 615 bool ThreadState::judgeGCThreshold(size_t allocatedObjectSizeThreshold, double h
eapGrowingRateThreshold) |
| 616 { |
| 617 // If the allocated object size is small enough, don't trigger a GC. |
| 618 if (Heap::allocatedObjectSize() < allocatedObjectSizeThreshold) |
| 619 return false; |
| 620 // If the heap growing rate is large enough, trigger a GC. |
| 621 return heapGrowingRate() >= heapGrowingRateThreshold; |
603 } | 622 } |
604 | 623 |
605 bool ThreadState::shouldForceMemoryPressureGC() | 624 bool ThreadState::shouldForceMemoryPressureGC() |
606 { | 625 { |
607 // Avoid potential overflow by truncating to Kb. | 626 if (currentObjectSize() < 300 * 1024 * 1024) |
608 size_t currentObjectSizeKb = currentObjectSize() >> 10; | |
609 if (currentObjectSizeKb < 300 * 1024) | |
610 return false; | 627 return false; |
611 | 628 |
612 size_t estimatedLiveObjectSizeKb = estimatedLiveObjectSize() >> 10; | |
613 // If we're consuming too much memory, trigger a conservative GC | 629 // If we're consuming too much memory, trigger a conservative GC |
614 // aggressively. This is a safe guard to avoid OOM. | 630 // aggressively. This is a safe guard to avoid OOM. |
615 return currentObjectSizeKb > (estimatedLiveObjectSizeKb * 3) / 2; | 631 return judgeGCThreshold(0, 1.5); |
616 } | 632 } |
617 | 633 |
618 // TODO(haraken): We should improve the GC heuristics. | |
619 // These heuristics affect performance significantly. | |
620 bool ThreadState::shouldScheduleIdleGC() | 634 bool ThreadState::shouldScheduleIdleGC() |
621 { | 635 { |
622 if (gcState() != NoGCScheduled) | 636 if (gcState() != NoGCScheduled) |
623 return false; | 637 return false; |
624 #if ENABLE(IDLE_GC) | 638 #if ENABLE(IDLE_GC) |
625 // Avoid potential overflow by truncating to Kb. | 639 return judgeGCThreshold(1024 * 1024, 1.5); |
626 size_t allocatedObjectSizeKb = Heap::allocatedObjectSize() >> 10; | |
627 // The estimated size is updated when the main thread finishes lazy | |
628 // sweeping. If this thread reaches here before the main thread finishes | |
629 // lazy sweeping, the thread will use the estimated size of the last GC. | |
630 size_t estimatedLiveObjectSizeKb = estimatedLiveObjectSize() >> 10; | |
631 // Heap::markedObjectSize() may be underestimated if any thread has not | |
632 // finished completeSweep(). | |
633 size_t currentObjectSizeKb = currentObjectSize() >> 10; | |
634 // Schedule an idle GC if Oilpan has allocated more than 1 MB since | |
635 // the last GC and the current memory usage is >50% larger than | |
636 // the estimated live memory usage. | |
637 return allocatedObjectSizeKb >= 1024 && currentObjectSizeKb > (estimatedLive
ObjectSizeKb * 3) / 2; | |
638 #else | 640 #else |
639 return false; | 641 return false; |
640 #endif | 642 #endif |
641 } | 643 } |
642 | 644 |
643 // TODO(haraken): We should improve the GC heuristics. | |
644 // These heuristics affect performance significantly. | |
645 bool ThreadState::shouldSchedulePreciseGC() | 645 bool ThreadState::shouldSchedulePreciseGC() |
646 { | 646 { |
647 if (gcState() != NoGCScheduled) | 647 if (gcState() != NoGCScheduled) |
648 return false; | 648 return false; |
649 #if ENABLE(IDLE_GC) | 649 #if ENABLE(IDLE_GC) |
650 return false; | 650 return false; |
651 #else | 651 #else |
652 // Avoid potential overflow by truncating to Kb. | 652 return judgeGCThreshold(1024 * 1024, 1.5); |
653 size_t allocatedObjectSizeKb = Heap::allocatedObjectSize() >> 10; | |
654 // The estimated size is updated when the main thread finishes lazy | |
655 // sweeping. If this thread reaches here before the main thread finishes | |
656 // lazy sweeping, the thread will use the estimated size of the last GC. | |
657 size_t estimatedLiveObjectSizeKb = estimatedLiveObjectSize() >> 10; | |
658 // Heap::markedObjectSize() may be underestimated if any thread has not | |
659 // finished completeSweep(). | |
660 size_t currentObjectSizeKb = currentObjectSize() >> 10; | |
661 // Schedule a precise GC if Oilpan has allocated more than 1 MB since | |
662 // the last GC and the current memory usage is >50% larger than | |
663 // the estimated live memory usage. | |
664 return allocatedObjectSizeKb >= 1024 && currentObjectSizeKb > (estimatedLive
ObjectSizeKb * 3) / 2; | |
665 #endif | 653 #endif |
666 } | 654 } |
667 | 655 |
668 bool ThreadState::shouldSchedulePageNavigationGC(float estimatedRemovalRatio) | 656 bool ThreadState::shouldSchedulePageNavigationGC(float estimatedRemovalRatio) |
669 { | 657 { |
670 if (UNLIKELY(isGCForbidden())) | 658 if (UNLIKELY(isGCForbidden())) |
671 return false; | 659 return false; |
672 | 660 |
673 if (shouldForceMemoryPressureGC()) | 661 if (shouldForceMemoryPressureGC()) |
674 return true; | 662 return true; |
675 | 663 |
676 // Avoid potential overflow by truncating to Kb. | 664 return judgeGCThreshold(1024 * 1024, 1.5); |
677 size_t allocatedObjectSizeKb = Heap::allocatedObjectSize() >> 10; | 665 } |
678 // The estimated size is updated when the main thread finishes lazy | 666 |
679 // sweeping. If this thread reaches here before the main thread finishes | 667 bool ThreadState::shouldForceConservativeGC() |
680 // lazy sweeping, the thread will use the estimated size of the last GC. | 668 { |
681 size_t estimatedLiveObjectSizeKb = (estimatedLiveObjectSize() >> 10) * (1 -
estimatedRemovalRatio); | 669 if (UNLIKELY(isGCForbidden())) |
682 // Heap::markedObjectSize() may be underestimated if any thread has not | 670 return false; |
683 // finished completeSweep(). | 671 |
684 size_t currentObjectSizeKb = currentObjectSize() >> 10; | 672 if (shouldForceMemoryPressureGC()) |
685 // Schedule a precise GC if Oilpan has allocated more than 1 MB since | 673 return true; |
686 // the last GC and the current memory usage is >50% larger than | 674 |
687 // the estimated live memory usage. | 675 // TODO(haraken): 400% is too large. Lower the heap growing factor. |
688 return allocatedObjectSizeKb >= 1024 && currentObjectSizeKb > (estimatedLive
ObjectSizeKb * 3) / 2; | 676 return judgeGCThreshold(32 * 1024 * 1024, 5.0); |
689 } | 677 } |
690 | 678 |
691 void ThreadState::schedulePageNavigationGCIfNeeded(float estimatedRemovalRatio) | 679 void ThreadState::schedulePageNavigationGCIfNeeded(float estimatedRemovalRatio) |
692 { | 680 { |
693 ASSERT(checkThread()); | 681 ASSERT(checkThread()); |
694 // Finish on-going lazy sweeping. | 682 // Finish on-going lazy sweeping. |
695 // TODO(haraken): It might not make sense to force completeSweep() for all | 683 // TODO(haraken): It might not make sense to force completeSweep() for all |
696 // page navigations. | 684 // page navigations. |
697 completeSweep(); | 685 completeSweep(); |
698 ASSERT(!isSweepingInProgress()); | 686 ASSERT(!isSweepingInProgress()); |
699 ASSERT(!sweepForbidden()); | 687 ASSERT(!sweepForbidden()); |
700 | 688 |
701 Heap::reportMemoryUsageForTracing(); | 689 Heap::reportMemoryUsageForTracing(); |
702 if (shouldSchedulePageNavigationGC(estimatedRemovalRatio)) | 690 if (shouldSchedulePageNavigationGC(estimatedRemovalRatio)) |
703 schedulePageNavigationGC(); | 691 schedulePageNavigationGC(); |
704 } | 692 } |
705 | 693 |
706 void ThreadState::schedulePageNavigationGC() | 694 void ThreadState::schedulePageNavigationGC() |
707 { | 695 { |
708 ASSERT(checkThread()); | 696 ASSERT(checkThread()); |
709 ASSERT(!isSweepingInProgress()); | 697 ASSERT(!isSweepingInProgress()); |
710 setGCState(PageNavigationGCScheduled); | 698 setGCState(PageNavigationGCScheduled); |
711 } | 699 } |
712 | 700 |
713 // TODO(haraken): We should improve the GC heuristics. | |
714 // These heuristics affect performance significantly. | |
715 bool ThreadState::shouldForceConservativeGC() | |
716 { | |
717 if (UNLIKELY(isGCForbidden())) | |
718 return false; | |
719 | |
720 if (shouldForceMemoryPressureGC()) | |
721 return true; | |
722 | |
723 // Avoid potential overflow by truncating to Kb. | |
724 size_t allocatedObjectSizeKb = Heap::allocatedObjectSize() >> 10; | |
725 // The estimated size is updated when the main thread finishes lazy | |
726 // sweeping. If this thread reaches here before the main thread finishes | |
727 // lazy sweeping, the thread will use the estimated size of the last GC. | |
728 size_t estimatedLiveObjectSizeKb = estimatedLiveObjectSize() >> 10; | |
729 // Heap::markedObjectSize() may be underestimated if any thread has not | |
730 // finished completeSweep(). | |
731 size_t currentObjectSizeKb = currentObjectSize() >> 10; | |
732 // Schedule a conservative GC if Oilpan has allocated more than 32 MB since | |
733 // the last GC and the current memory usage is >400% larger than | |
734 // the estimated live memory usage. | |
735 // TODO(haraken): 400% is too large. Lower the heap growing factor. | |
736 return allocatedObjectSizeKb >= 32 * 1024 && currentObjectSizeKb > 5 * estim
atedLiveObjectSizeKb; | |
737 } | |
738 | |
739 void ThreadState::scheduleGCIfNeeded() | 701 void ThreadState::scheduleGCIfNeeded() |
740 { | 702 { |
741 ASSERT(checkThread()); | 703 ASSERT(checkThread()); |
742 // Allocation is allowed during sweeping, but those allocations should not | 704 // Allocation is allowed during sweeping, but those allocations should not |
743 // trigger nested GCs. | 705 // trigger nested GCs. |
744 if (isSweepingInProgress()) | 706 if (isSweepingInProgress()) |
745 return; | 707 return; |
746 ASSERT(!sweepForbidden()); | 708 ASSERT(!sweepForbidden()); |
747 | 709 |
748 Heap::reportMemoryUsageForTracing(); | 710 Heap::reportMemoryUsageForTracing(); |
(...skipping 883 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1632 json->beginArray(it->key.ascii().data()); | 1594 json->beginArray(it->key.ascii().data()); |
1633 for (size_t age = 0; age <= maxHeapObjectAge; ++age) | 1595 for (size_t age = 0; age <= maxHeapObjectAge; ++age) |
1634 json->pushInteger(it->value.ages[age]); | 1596 json->pushInteger(it->value.ages[age]); |
1635 json->endArray(); | 1597 json->endArray(); |
1636 } | 1598 } |
1637 TRACE_EVENT_OBJECT_SNAPSHOT_WITH_ID(TRACE_DISABLED_BY_DEFAULT("blink_gc"), s
tatsName, this, json.release()); | 1599 TRACE_EVENT_OBJECT_SNAPSHOT_WITH_ID(TRACE_DISABLED_BY_DEFAULT("blink_gc"), s
tatsName, this, json.release()); |
1638 } | 1600 } |
1639 #endif | 1601 #endif |
1640 | 1602 |
1641 } // namespace blink | 1603 } // namespace blink |
OLD | NEW |