| OLD | NEW |
| 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file |
| 2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
| 3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
| 4 | 4 |
| 5 #include "vm/pages.h" | 5 #include "vm/pages.h" |
| 6 | 6 |
| 7 #include "platform/assert.h" | 7 #include "platform/assert.h" |
| 8 #include "vm/compiler_stats.h" | 8 #include "vm/compiler_stats.h" |
| 9 #include "vm/gc_marker.h" | 9 #include "vm/gc_marker.h" |
| 10 #include "vm/gc_sweeper.h" | 10 #include "vm/gc_sweeper.h" |
| 11 #include "vm/lockers.h" | 11 #include "vm/lockers.h" |
| 12 #include "vm/object.h" | 12 #include "vm/object.h" |
| 13 #include "vm/thread.h" | 13 #include "vm/thread.h" |
| 14 #include "vm/virtual_memory.h" | 14 #include "vm/virtual_memory.h" |
| 15 | 15 |
| 16 namespace dart { | 16 namespace dart { |
| 17 | 17 |
| 18 DEFINE_FLAG(int, heap_growth_space_ratio, 20, | 18 DEFINE_FLAG(int, heap_growth_space_ratio, 20, |
| 19 "The desired maximum percentage of free space after GC"); | 19 "The desired maximum percentage of free space after GC"); |
| 20 DEFINE_FLAG(int, heap_growth_time_ratio, 3, | 20 DEFINE_FLAG(int, heap_growth_time_ratio, 3, |
| 21 "The desired maximum percentage of time spent in GC"); | 21 "The desired maximum percentage of time spent in GC"); |
| 22 DEFINE_FLAG(int, heap_growth_rate, 256, | 22 DEFINE_FLAG(int, heap_growth_rate, 280, |
| 23 "The max number of pages the heap can grow at a time"); | 23 "The max number of pages the heap can grow at a time"); |
| 24 DEFINE_FLAG(bool, print_free_list_before_gc, false, | 24 DEFINE_FLAG(bool, print_free_list_before_gc, false, |
| 25 "Print free list statistics before a GC"); | 25 "Print free list statistics before a GC"); |
| 26 DEFINE_FLAG(bool, print_free_list_after_gc, false, | 26 DEFINE_FLAG(bool, print_free_list_after_gc, false, |
| 27 "Print free list statistics after a GC"); | 27 "Print free list statistics after a GC"); |
| 28 DEFINE_FLAG(bool, collect_code, true, | 28 DEFINE_FLAG(bool, collect_code, true, |
| 29 "Attempt to GC infrequently used code."); | 29 "Attempt to GC infrequently used code."); |
| 30 DEFINE_FLAG(int, code_collection_interval_in_us, 30000000, | 30 DEFINE_FLAG(int, code_collection_interval_in_us, 30000000, |
| 31 "Time between attempts to collect unused code."); | 31 "Time between attempts to collect unused code."); |
| 32 DEFINE_FLAG(bool, log_code_drop, false, | 32 DEFINE_FLAG(bool, log_code_drop, false, |
| 33 "Emit a log message when pointers to unused code are dropped."); | 33 "Emit a log message when pointers to unused code are dropped."); |
| 34 DEFINE_FLAG(bool, always_drop_code, false, | 34 DEFINE_FLAG(bool, always_drop_code, false, |
| 35 "Always try to drop code if the function's usage counter is >= 0"); | 35 "Always try to drop code if the function's usage counter is >= 0"); |
| 36 DEFINE_FLAG(bool, concurrent_sweep, false, | 36 DEFINE_FLAG(bool, concurrent_sweep, false, |
| 37 "Concurrent sweep for old generation."); | 37 "Concurrent sweep for old generation."); |
| 38 DEFINE_FLAG(bool, log_growth, false, "Log PageSpace growth policy decisions."); |
| 38 | 39 |
| 39 HeapPage* HeapPage::Initialize(VirtualMemory* memory, PageType type) { | 40 HeapPage* HeapPage::Initialize(VirtualMemory* memory, PageType type) { |
| 40 ASSERT(memory->size() > VirtualMemory::PageSize()); | 41 ASSERT(memory->size() > VirtualMemory::PageSize()); |
| 41 bool is_executable = (type == kExecutable); | 42 bool is_executable = (type == kExecutable); |
| 42 memory->Commit(is_executable); | 43 memory->Commit(is_executable); |
| 43 | 44 |
| 44 HeapPage* result = reinterpret_cast<HeapPage*>(memory->address()); | 45 HeapPage* result = reinterpret_cast<HeapPage*>(memory->address()); |
| 45 result->memory_ = memory; | 46 result->memory_ = memory; |
| 46 result->next_ = NULL; | 47 result->next_ = NULL; |
| 47 result->executable_ = is_executable; | 48 result->executable_ = is_executable; |
| (...skipping 74 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 122 | 123 |
| 123 PageSpace::PageSpace(Heap* heap, intptr_t max_capacity_in_words) | 124 PageSpace::PageSpace(Heap* heap, intptr_t max_capacity_in_words) |
| 124 : freelist_(), | 125 : freelist_(), |
| 125 heap_(heap), | 126 heap_(heap), |
| 126 pages_lock_(new Mutex()), | 127 pages_lock_(new Mutex()), |
| 127 pages_(NULL), | 128 pages_(NULL), |
| 128 pages_tail_(NULL), | 129 pages_tail_(NULL), |
| 129 exec_pages_(NULL), | 130 exec_pages_(NULL), |
| 130 exec_pages_tail_(NULL), | 131 exec_pages_tail_(NULL), |
| 131 large_pages_(NULL), | 132 large_pages_(NULL), |
| 133 bump_top_(0), |
| 134 bump_end_(0), |
| 132 max_capacity_in_words_(max_capacity_in_words), | 135 max_capacity_in_words_(max_capacity_in_words), |
| 133 tasks_lock_(new Monitor()), | 136 tasks_lock_(new Monitor()), |
| 134 tasks_(0), | 137 tasks_(0), |
| 135 page_space_controller_(heap, | 138 page_space_controller_(heap, |
| 136 FLAG_heap_growth_space_ratio, | 139 FLAG_heap_growth_space_ratio, |
| 137 FLAG_heap_growth_rate, | 140 FLAG_heap_growth_rate, |
| 138 FLAG_heap_growth_time_ratio), | 141 FLAG_heap_growth_time_ratio), |
| 139 gc_time_micros_(0), | 142 gc_time_micros_(0), |
| 140 collections_(0) { | 143 collections_(0) { |
| 141 } | 144 } |
| (...skipping 132 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 274 void PageSpace::FreePages(HeapPage* pages) { | 277 void PageSpace::FreePages(HeapPage* pages) { |
| 275 HeapPage* page = pages; | 278 HeapPage* page = pages; |
| 276 while (page != NULL) { | 279 while (page != NULL) { |
| 277 HeapPage* next = page->next(); | 280 HeapPage* next = page->next(); |
| 278 page->Deallocate(); | 281 page->Deallocate(); |
| 279 page = next; | 282 page = next; |
| 280 } | 283 } |
| 281 } | 284 } |
| 282 | 285 |
| 283 | 286 |
| 287 uword PageSpace::TryAllocateInFreshPage(intptr_t size, |
| 288 HeapPage::PageType type, |
| 289 GrowthPolicy growth_policy, |
| 290 bool is_locked) { |
| 291 ASSERT(size < kAllocatablePageSize); |
| 292 uword result = 0; |
| 293 SpaceUsage after_allocation = usage_; |
| 294 after_allocation.used_in_words += size >> kWordSizeLog2; |
| 295 // Can we grow by one page? |
| 296 after_allocation.capacity_in_words += kPageSizeInWords; |
| 297 if ((growth_policy == kForceGrowth || |
| 298 !page_space_controller_.NeedsGarbageCollection(after_allocation)) && |
| 299 CanIncreaseCapacityInWords(kPageSizeInWords)) { |
| 300 HeapPage* page = AllocatePage(type); |
| 301 ASSERT(page != NULL); |
| 302 // Start of the newly allocated page is the allocated object. |
| 303 result = page->object_start(); |
| 304 usage_ = after_allocation; |
| 305 // Enqueue the remainder in the free list. |
| 306 uword free_start = result + size; |
| 307 intptr_t free_size = page->object_end() - free_start; |
| 308 if (free_size > 0) { |
| 309 if (is_locked) { |
| 310 freelist_[type].FreeLocked(free_start, free_size); |
| 311 } else { |
| 312 freelist_[type].Free(free_start, free_size); |
| 313 } |
| 314 } |
| 315 } |
| 316 return result; |
| 317 } |
| 318 |
| 319 |
| 284 uword PageSpace::TryAllocateInternal(intptr_t size, | 320 uword PageSpace::TryAllocateInternal(intptr_t size, |
| 285 HeapPage::PageType type, | 321 HeapPage::PageType type, |
| 286 GrowthPolicy growth_policy, | 322 GrowthPolicy growth_policy, |
| 287 bool is_protected, | 323 bool is_protected, |
| 288 bool is_locked) { | 324 bool is_locked) { |
| 289 ASSERT(size >= kObjectAlignment); | 325 ASSERT(size >= kObjectAlignment); |
| 290 ASSERT(Utils::IsAligned(size, kObjectAlignment)); | 326 ASSERT(Utils::IsAligned(size, kObjectAlignment)); |
| 291 uword result = 0; | 327 uword result = 0; |
| 292 SpaceUsage after_allocation = usage_; | |
| 293 after_allocation.used_in_words += size >> kWordSizeLog2; | |
| 294 if (size < kAllocatablePageSize) { | 328 if (size < kAllocatablePageSize) { |
| 295 if (is_locked) { | 329 if (is_locked) { |
| 296 result = freelist_[type].TryAllocateLocked(size, is_protected); | 330 result = freelist_[type].TryAllocateLocked(size, is_protected); |
| 297 } else { | 331 } else { |
| 298 result = freelist_[type].TryAllocate(size, is_protected); | 332 result = freelist_[type].TryAllocate(size, is_protected); |
| 299 } | 333 } |
| 300 if (result == 0) { | 334 if (result == 0) { |
| 301 // Can we grow by one page? | 335 result = TryAllocateInFreshPage(size, type, growth_policy, is_locked); |
| 302 after_allocation.capacity_in_words += kPageSizeInWords; | |
| 303 if ((!page_space_controller_.NeedsGarbageCollection(after_allocation) || | |
| 304 growth_policy == kForceGrowth) && | |
| 305 CanIncreaseCapacityInWords(kPageSizeInWords)) { | |
| 306 HeapPage* page = AllocatePage(type); | |
| 307 ASSERT(page != NULL); | |
| 308 // Start of the newly allocated page is the allocated object. | |
| 309 result = page->object_start(); | |
| 310 // Enqueue the remainder in the free list. | |
| 311 uword free_start = result + size; | |
| 312 intptr_t free_size = page->object_end() - free_start; | |
| 313 if (free_size > 0) { | |
| 314 if (is_locked) { | |
| 315 freelist_[type].FreeLocked(free_start, free_size); | |
| 316 } else { | |
| 317 freelist_[type].Free(free_start, free_size); | |
| 318 } | |
| 319 } | |
| 320 } | |
| 321 } | 336 } |
| 322 } else { | 337 } else { |
| 323 // Large page allocation. | 338 // Large page allocation. |
| 324 intptr_t page_size_in_words = LargePageSizeInWordsFor(size); | 339 intptr_t page_size_in_words = LargePageSizeInWordsFor(size); |
| 325 if ((page_size_in_words << kWordSizeLog2) < size) { | 340 if ((page_size_in_words << kWordSizeLog2) < size) { |
| 326 // On overflow we fail to allocate. | 341 // On overflow we fail to allocate. |
| 327 return 0; | 342 return 0; |
| 328 } | 343 } |
| 344 SpaceUsage after_allocation = usage_; |
| 345 after_allocation.used_in_words += size >> kWordSizeLog2; |
| 329 after_allocation.capacity_in_words += page_size_in_words; | 346 after_allocation.capacity_in_words += page_size_in_words; |
| 330 if ((!page_space_controller_.NeedsGarbageCollection(after_allocation) || | 347 if ((growth_policy == kForceGrowth || |
| 331 growth_policy == kForceGrowth) && | 348 !page_space_controller_.NeedsGarbageCollection(after_allocation)) && |
| 332 CanIncreaseCapacityInWords(page_size_in_words)) { | 349 CanIncreaseCapacityInWords(page_size_in_words)) { |
| 333 HeapPage* page = AllocateLargePage(size, type); | 350 HeapPage* page = AllocateLargePage(size, type); |
| 334 if (page != NULL) { | 351 if (page != NULL) { |
| 335 result = page->object_start(); | 352 result = page->object_start(); |
| 353 usage_ = after_allocation; |
| 336 } | 354 } |
| 337 } | 355 } |
| 338 } | 356 } |
| 339 if (result != 0) { | 357 if (result != 0) { |
| 340 usage_ = after_allocation; | |
| 341 if (FLAG_compiler_stats && (type == HeapPage::kExecutable)) { | 358 if (FLAG_compiler_stats && (type == HeapPage::kExecutable)) { |
| 342 CompilerStats::code_allocated += size; | 359 CompilerStats::code_allocated += size; |
| 343 } | 360 } |
| 344 } | 361 } |
| 345 ASSERT((result & kObjectAlignmentMask) == kOldObjectAlignmentOffset); | 362 ASSERT((result & kObjectAlignmentMask) == kOldObjectAlignmentOffset); |
| 346 return result; | 363 return result; |
| 347 } | 364 } |
| 348 | 365 |
| 349 | 366 |
| 350 void PageSpace::AcquireDataLock() { | 367 void PageSpace::AcquireDataLock() { |
| (...skipping 270 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 621 SpaceUsage usage_before = usage_; | 638 SpaceUsage usage_before = usage_; |
| 622 | 639 |
| 623 // Mark all reachable old-gen objects. | 640 // Mark all reachable old-gen objects. |
| 624 bool collect_code = FLAG_collect_code && ShouldCollectCode(); | 641 bool collect_code = FLAG_collect_code && ShouldCollectCode(); |
| 625 GCMarker marker(heap_); | 642 GCMarker marker(heap_); |
| 626 marker.MarkObjects(isolate, this, invoke_api_callbacks, collect_code); | 643 marker.MarkObjects(isolate, this, invoke_api_callbacks, collect_code); |
| 627 usage_.used_in_words = marker.marked_words(); | 644 usage_.used_in_words = marker.marked_words(); |
| 628 | 645 |
| 629 int64_t mid1 = OS::GetCurrentTimeMicros(); | 646 int64_t mid1 = OS::GetCurrentTimeMicros(); |
| 630 | 647 |
| 631 // Reset the bump allocation page to unused. | 648 // Abandon the remainder of the bump allocation block. |
| 649 bump_top_ = 0; |
| 650 bump_end_ = 0; |
| 632 // Reset the freelists and setup sweeping. | 651 // Reset the freelists and setup sweeping. |
| 633 freelist_[HeapPage::kData].Reset(); | 652 freelist_[HeapPage::kData].Reset(); |
| 634 freelist_[HeapPage::kExecutable].Reset(); | 653 freelist_[HeapPage::kExecutable].Reset(); |
| 635 | 654 |
| 636 int64_t mid2 = OS::GetCurrentTimeMicros(); | 655 int64_t mid2 = OS::GetCurrentTimeMicros(); |
| 637 int64_t mid3 = 0; | 656 int64_t mid3 = 0; |
| 638 | 657 |
| 639 { | 658 { |
| 640 GCSweeper sweeper(heap_); | 659 GCSweeper sweeper(heap_); |
| 641 | 660 |
| (...skipping 86 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 728 | 747 |
| 729 // Done, reset the task count. | 748 // Done, reset the task count. |
| 730 { | 749 { |
| 731 MonitorLocker ml(tasks_lock()); | 750 MonitorLocker ml(tasks_lock()); |
| 732 set_tasks(tasks() - 1); | 751 set_tasks(tasks() - 1); |
| 733 ml.Notify(); | 752 ml.Notify(); |
| 734 } | 753 } |
| 735 } | 754 } |
| 736 | 755 |
| 737 | 756 |
| 757 uword PageSpace::TryAllocateDataBump(intptr_t size, |
| 758 GrowthPolicy growth_policy) { |
| 759 ASSERT(size >= kObjectAlignment); |
| 760 ASSERT(Utils::IsAligned(size, kObjectAlignment)); |
| 761 intptr_t remaining = bump_end_ - bump_top_; |
| 762 if (remaining < size) { |
| 763 // Checking this first would be logical, but needlessly slow. |
| 764 if (size >= kAllocatablePageSize) { |
| 765 return TryAllocate(size, HeapPage::kData, growth_policy); |
| 766 } |
| 767 FreeListElement* block = freelist_[HeapPage::kData].TryAllocateLarge(size); |
| 768 if (block == NULL) { |
| 769 // Allocating from a new page (if growth policy allows) will have the |
| 770 // side-effect of populating the freelist with a large block. The next |
| 771 // bump allocation request will have a chance to consume that block. |
| 772 // TODO(koda): Could take freelist lock just once instead of twice. |
| 773 return TryAllocateInFreshPage(size, |
| 774 HeapPage::kData, |
| 775 growth_policy, |
| 776 /* is_locked = */ false); |
| 777 } |
| 778 intptr_t block_size = block->Size(); |
| 779 bump_top_ = reinterpret_cast<uword>(block); |
| 780 bump_end_ = bump_top_ + block_size; |
| 781 remaining = block_size; |
| 782 } |
| 783 ASSERT(remaining >= size); |
| 784 uword result = bump_top_; |
| 785 bump_top_ += size; |
| 786 usage_.used_in_words += size >> kWordSizeLog2; |
| 787 remaining -= size; |
| 788 if (remaining > 0) { |
| 789 FreeListElement::AsElement(bump_top_, remaining); |
| 790 } |
| 791 return result; |
| 792 } |
| 793 |
| 794 |
| 738 PageSpaceController::PageSpaceController(Heap* heap, | 795 PageSpaceController::PageSpaceController(Heap* heap, |
| 739 int heap_growth_ratio, | 796 int heap_growth_ratio, |
| 740 int heap_growth_max, | 797 int heap_growth_max, |
| 741 int garbage_collection_time_ratio) | 798 int garbage_collection_time_ratio) |
| 742 : heap_(heap), | 799 : heap_(heap), |
| 743 is_enabled_(false), | 800 is_enabled_(false), |
| 744 grow_heap_(heap_growth_max / 2), | 801 grow_heap_(heap_growth_max / 2), |
| 745 heap_growth_ratio_(heap_growth_ratio), | 802 heap_growth_ratio_(heap_growth_ratio), |
| 746 desired_utilization_((100.0 - heap_growth_ratio) / 100.0), | 803 desired_utilization_((100.0 - heap_growth_ratio) / 100.0), |
| 747 heap_growth_max_(heap_growth_max), | 804 heap_growth_max_(heap_growth_max), |
| (...skipping 25 matching lines...) Expand all Loading... |
| 773 // To avoid waste, the first GC should be triggered before too long. After | 830 // To avoid waste, the first GC should be triggered before too long. After |
| 774 // kInitialTimeoutSeconds, gradually lower the capacity limit. | 831 // kInitialTimeoutSeconds, gradually lower the capacity limit. |
| 775 static const double kInitialTimeoutSeconds = 1.00; | 832 static const double kInitialTimeoutSeconds = 1.00; |
| 776 if (history_.IsEmpty()) { | 833 if (history_.IsEmpty()) { |
| 777 double seconds_since_init = MicrosecondsToSeconds( | 834 double seconds_since_init = MicrosecondsToSeconds( |
| 778 OS::GetCurrentTimeMicros() - heap_->isolate()->start_time()); | 835 OS::GetCurrentTimeMicros() - heap_->isolate()->start_time()); |
| 779 if (seconds_since_init > kInitialTimeoutSeconds) { | 836 if (seconds_since_init > kInitialTimeoutSeconds) { |
| 780 multiplier *= seconds_since_init / kInitialTimeoutSeconds; | 837 multiplier *= seconds_since_init / kInitialTimeoutSeconds; |
| 781 } | 838 } |
| 782 } | 839 } |
| 783 return capacity_increase_in_pages * multiplier > grow_heap_; | 840 bool needs_gc = capacity_increase_in_pages * multiplier > grow_heap_; |
| 841 if (FLAG_log_growth) { |
| 842 OS::PrintErr("%s: %" Pd " * %f %s %" Pd "\n", |
| 843 needs_gc ? "NEEDS GC" : "grow", |
| 844 capacity_increase_in_pages, |
| 845 multiplier, |
| 846 needs_gc ? ">" : "<=", |
| 847 grow_heap_); |
| 848 } |
| 849 return needs_gc; |
| 784 } | 850 } |
| 785 | 851 |
| 786 | 852 |
| 787 void PageSpaceController::EvaluateGarbageCollection( | 853 void PageSpaceController::EvaluateGarbageCollection( |
| 788 SpaceUsage before, SpaceUsage after, int64_t start, int64_t end) { | 854 SpaceUsage before, SpaceUsage after, int64_t start, int64_t end) { |
| 789 ASSERT(end >= start); | 855 ASSERT(end >= start); |
| 790 history_.AddGarbageCollectionTime(start, end); | 856 history_.AddGarbageCollectionTime(start, end); |
| 791 int gc_time_fraction = history_.GarbageCollectionTimeFraction(); | 857 int gc_time_fraction = history_.GarbageCollectionTimeFraction(); |
| 792 heap_->RecordData(PageSpace::kGCTimeFraction, gc_time_fraction); | 858 heap_->RecordData(PageSpace::kGCTimeFraction, gc_time_fraction); |
| 793 | 859 |
| (...skipping 57 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 851 return 0; | 917 return 0; |
| 852 } else { | 918 } else { |
| 853 ASSERT(total_time >= gc_time); | 919 ASSERT(total_time >= gc_time); |
| 854 int result= static_cast<int>((static_cast<double>(gc_time) / | 920 int result= static_cast<int>((static_cast<double>(gc_time) / |
| 855 static_cast<double>(total_time)) * 100); | 921 static_cast<double>(total_time)) * 100); |
| 856 return result; | 922 return result; |
| 857 } | 923 } |
| 858 } | 924 } |
| 859 | 925 |
| 860 } // namespace dart | 926 } // namespace dart |
| OLD | NEW |