diff options
author | Mathieu Chartier <mathieuc@google.com> | 2014-04-08 14:40:03 -0700 |
---|---|---|
committer | Mathieu Chartier <mathieuc@google.com> | 2014-04-11 14:47:35 -0700 |
commit | 31f441464c0c8f840aba37e236ad133f30308d70 (patch) | |
tree | 54dba5521790d0038478f7cdf5103299d5149970 /runtime/gc | |
parent | 9b417e4f0f87da6bfe8dc5f02c987acfcb6dca31 (diff) | |
download | art-31f441464c0c8f840aba37e236ad133f30308d70.zip art-31f441464c0c8f840aba37e236ad133f30308d70.tar.gz art-31f441464c0c8f840aba37e236ad133f30308d70.tar.bz2 |
Clean-up heap spaces.
We now use the CMS collector instead of the semispace collector when
the phone is booting. We still perform compaction during the zygote
space creation. This reduces time spent in GC by ~2s during boot
and doesn't affect zygote space size.
Changed the space creation logic to create the temp space when a
background transition occurs.
Added a flag to each space which is true if you are allowed to
move objects that are within this space.
Removed SwapSemiSpaces call from the semi space collector, it is now
the job of the caller to do this with threads suspended. This
simplifies the logic in the zygote compaction / heap transition code
since these do not copy from one semispace to another.
Added Space::Clear to RosAllocSpace and DlMallocSpace. This greatly
simplifies the code used for collector transitions.
Time spent in GC creating zygote space:
Before: 3.4s, After: 1.28s
No change in zygote space size.
Bug: 13878055
Change-Id: I700348ab7d5bf3aa537c0cd70c0fed09aa4b0623
Diffstat (limited to 'runtime/gc')
25 files changed, 316 insertions, 287 deletions
diff --git a/runtime/gc/collector/semi_space.cc b/runtime/gc/collector/semi_space.cc index e82d533..6e31cb7 100644 --- a/runtime/gc/collector/semi_space.cc +++ b/runtime/gc/collector/semi_space.cc @@ -63,7 +63,6 @@ namespace gc { namespace collector { static constexpr bool kProtectFromSpace = true; -static constexpr bool kClearFromSpace = true; static constexpr bool kStoreStackTraces = false; static constexpr bool kUseBytesPromoted = true; static constexpr size_t kBytesPromotedThreshold = 4 * MB; @@ -122,6 +121,7 @@ void SemiSpace::InitializePhase() { // Do any pre GC verification. timings_.NewSplit("PreGcVerification"); heap_->PreGcVerification(this); + CHECK(from_space_->CanMoveObjects()) << "Attempting to move from " << *from_space_; // Set the initial bitmap. to_space_live_bitmap_ = to_space_->GetLiveBitmap(); } @@ -182,9 +182,6 @@ void SemiSpace::MarkingPhase() { Locks::mutator_lock_->AssertExclusiveHeld(self_); TimingLogger::ScopedSplit split("MarkingPhase", &timings_); - // Need to do this with mutators paused so that somebody doesn't accidentally allocate into the - // wrong space. - heap_->SwapSemiSpaces(); if (generational_) { // If last_gc_to_space_end_ is out of the bounds of the from-space // (the to-space from last GC), then point it to the beginning of @@ -414,11 +411,7 @@ void SemiSpace::ReclaimPhase() { TimingLogger::ScopedSplit split("UnBindBitmaps", &timings_); GetHeap()->UnBindBitmaps(); } - if (kClearFromSpace) { - // Release the memory used by the from space. - from_space_->Clear(); - } - from_space_->Reset(); + from_space_->Clear(); // Protect the from space. VLOG(heap) << "Protecting space " << *from_space_; if (kProtectFromSpace) { diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc index fcf9fe9..efd1c7a 100644 --- a/runtime/gc/heap.cc +++ b/runtime/gc/heap.cc @@ -81,10 +81,15 @@ static constexpr size_t kMaxConcurrentRemainingBytes = 512 * KB; // relative to partial/full GC. This is desirable since sticky GCs interfere less with mutator // threads (lower pauses, use less memory bandwidth). static constexpr double kStickyGcThroughputAdjustment = 1.25; +// Whether or not we use the free list large object space. +static constexpr bool kUseFreeListSpaceForLOS = false; +// Whtehr or not we compact the zygote in PreZygoteFork. +static constexpr bool kCompactZygote = kMovingCollector; +static constexpr size_t kNonMovingSpaceCapacity = 64 * MB; Heap::Heap(size_t initial_size, size_t growth_limit, size_t min_free, size_t max_free, double target_utilization, size_t capacity, const std::string& image_file_name, - CollectorType post_zygote_collector_type, CollectorType background_collector_type, + CollectorType foreground_collector_type, CollectorType background_collector_type, size_t parallel_gc_threads, size_t conc_gc_threads, bool low_memory_mode, size_t long_pause_log_threshold, size_t long_gc_log_threshold, bool ignore_max_footprint, bool use_tlab, bool verify_pre_gc_heap, @@ -95,9 +100,9 @@ Heap::Heap(size_t initial_size, size_t growth_limit, size_t min_free, size_t max dlmalloc_space_(nullptr), main_space_(nullptr), collector_type_(kCollectorTypeNone), - post_zygote_collector_type_(post_zygote_collector_type), + foreground_collector_type_(foreground_collector_type), background_collector_type_(background_collector_type), - desired_collector_type_(collector_type_), + desired_collector_type_(foreground_collector_type_), heap_trim_request_lock_(nullptr), last_trim_time_(0), heap_transition_target_time_(0), @@ -162,15 +167,11 @@ Heap::Heap(size_t initial_size, size_t growth_limit, size_t min_free, size_t max // If we aren't the zygote, switch to the default non zygote allocator. This may update the // entrypoints. if (!is_zygote) { - desired_collector_type_ = post_zygote_collector_type_; large_object_threshold_ = kDefaultLargeObjectThreshold; - } else { - if (kMovingCollector) { - // We are the zygote, use bump pointer allocation + semi space collector. - bool generational = post_zygote_collector_type_ == kCollectorTypeGSS; - desired_collector_type_ = generational ? kCollectorTypeGSS : kCollectorTypeSS; - } else { - desired_collector_type_ = post_zygote_collector_type_; + // Background compaction is currently not supported for command line runs. + if (background_collector_type_ != foreground_collector_type_) { + LOG(WARNING) << "Disabling background compaction for non zygote"; + background_collector_type_ = foreground_collector_type_; } } ChangeCollector(desired_collector_type_); @@ -187,73 +188,56 @@ Heap::Heap(size_t initial_size, size_t growth_limit, size_t min_free, size_t max // isn't going to get in the middle byte* oat_file_end_addr = image_space->GetImageHeader().GetOatFileEnd(); CHECK_GT(oat_file_end_addr, image_space->End()); - if (oat_file_end_addr > requested_alloc_space_begin) { - requested_alloc_space_begin = AlignUp(oat_file_end_addr, kPageSize); - } + requested_alloc_space_begin = AlignUp(oat_file_end_addr, kPageSize); } - MemMap* malloc_space_mem_map = nullptr; - const char* malloc_space_name = is_zygote ? "zygote space" : "alloc space"; if (is_zygote) { - // Allocate a single mem map that is split into the malloc space - // and the post zygote non-moving space to put them adjacent. - size_t post_zygote_non_moving_space_size = 64 * MB; - size_t non_moving_spaces_size = capacity + post_zygote_non_moving_space_size; + // Reserve the address range before we create the non moving space to make sure bitmaps don't + // take it. std::string error_str; - malloc_space_mem_map = MemMap::MapAnonymous(malloc_space_name, requested_alloc_space_begin, - non_moving_spaces_size, PROT_READ | PROT_WRITE, - true, &error_str); - CHECK(malloc_space_mem_map != nullptr) << error_str; - post_zygote_non_moving_space_mem_map_.reset(malloc_space_mem_map->RemapAtEnd( - malloc_space_mem_map->Begin() + capacity, "post zygote non-moving space", - PROT_READ | PROT_WRITE, &error_str)); - CHECK(post_zygote_non_moving_space_mem_map_.get() != nullptr) << error_str; - VLOG(heap) << "malloc space mem map : " << malloc_space_mem_map; - VLOG(heap) << "post zygote non-moving space mem map : " - << post_zygote_non_moving_space_mem_map_.get(); + MemMap* mem_map = MemMap::MapAnonymous( + "main space", requested_alloc_space_begin + kNonMovingSpaceCapacity, capacity, + PROT_READ | PROT_WRITE, true, &error_str); + CHECK(mem_map != nullptr) << error_str; + // Non moving space is always dlmalloc since we currently don't have support for multiple + // rosalloc spaces. + non_moving_space_ = space::DlMallocSpace::Create( + "zygote / non moving space", initial_size, kNonMovingSpaceCapacity, kNonMovingSpaceCapacity, + requested_alloc_space_begin, false); + non_moving_space_->SetGrowthLimit(non_moving_space_->Capacity()); + CreateMainMallocSpace(mem_map, initial_size, growth_limit, capacity); } else { - // Allocate a mem map for the malloc space. std::string error_str; - malloc_space_mem_map = MemMap::MapAnonymous(malloc_space_name, requested_alloc_space_begin, - capacity, PROT_READ | PROT_WRITE, true, &error_str); - CHECK(malloc_space_mem_map != nullptr) << error_str; - VLOG(heap) << "malloc space mem map : " << malloc_space_mem_map; - } - CHECK(malloc_space_mem_map != nullptr); - space::MallocSpace* malloc_space; - if (kUseRosAlloc) { - malloc_space = space::RosAllocSpace::CreateFromMemMap(malloc_space_mem_map, malloc_space_name, - kDefaultStartingSize, initial_size, - growth_limit, capacity, low_memory_mode_); - CHECK(malloc_space != nullptr) << "Failed to create rosalloc space"; - } else { - malloc_space = space::DlMallocSpace::CreateFromMemMap(malloc_space_mem_map, malloc_space_name, - kDefaultStartingSize, initial_size, - growth_limit, capacity); - CHECK(malloc_space != nullptr) << "Failed to create dlmalloc space"; - } - VLOG(heap) << "malloc_space : " << malloc_space; + MemMap* mem_map = MemMap::MapAnonymous("main/non-moving space", requested_alloc_space_begin, + capacity, PROT_READ | PROT_WRITE, true, &error_str); + CHECK(mem_map != nullptr) << error_str; + // Create the main free list space, which doubles as the non moving space. We can do this since + // non zygote means that we won't have any background compaction. + CreateMainMallocSpace(mem_map, initial_size, growth_limit, capacity); + non_moving_space_ = main_space_; + } + CHECK(non_moving_space_ != nullptr); + + // We need to create the bump pointer if the foreground collector is a compacting GC. We only + // create the bump pointer space if we are not a moving foreground collector but have a moving + // background collector since the heap transition code will create the temp space by recycling + // the bitmap from the main space. if (kMovingCollector) { // TODO: Place bump-pointer spaces somewhere to minimize size of card table. - // TODO: Having 3+ spaces as big as the large heap size can cause virtual memory fragmentation - // issues. - const size_t bump_pointer_space_size = std::min(malloc_space->Capacity(), 128 * MB); - bump_pointer_space_ = space::BumpPointerSpace::Create("Bump pointer space", - bump_pointer_space_size, nullptr); + bump_pointer_space_ = space::BumpPointerSpace::Create("Bump pointer space", capacity, nullptr); CHECK(bump_pointer_space_ != nullptr) << "Failed to create bump pointer space"; AddSpace(bump_pointer_space_); - temp_space_ = space::BumpPointerSpace::Create("Bump pointer space 2", bump_pointer_space_size, - nullptr); + temp_space_ = space::BumpPointerSpace::Create("Bump pointer space 2", capacity, nullptr); CHECK(temp_space_ != nullptr) << "Failed to create bump pointer space"; AddSpace(temp_space_); - VLOG(heap) << "bump_pointer_space : " << bump_pointer_space_; - VLOG(heap) << "temp_space : " << temp_space_; } - non_moving_space_ = malloc_space; - malloc_space->SetFootprintLimit(malloc_space->Capacity()); - AddSpace(malloc_space); + if (non_moving_space_ != main_space_) { + AddSpace(non_moving_space_); + } + if (main_space_ != nullptr) { + AddSpace(main_space_); + } // Allocate the large object space. - constexpr bool kUseFreeListSpaceForLOS = false; if (kUseFreeListSpaceForLOS) { large_object_space_ = space::FreeListSpace::Create("large object space", nullptr, capacity); } else { @@ -268,11 +252,6 @@ Heap::Heap(size_t initial_size, size_t growth_limit, size_t min_free, size_t max // Relies on the spaces being sorted. byte* heap_begin = continuous_spaces_.front()->Begin(); byte* heap_end = continuous_spaces_.back()->Limit(); - if (is_zygote) { - CHECK(post_zygote_non_moving_space_mem_map_.get() != nullptr); - heap_begin = std::min(post_zygote_non_moving_space_mem_map_->Begin(), heap_begin); - heap_end = std::max(post_zygote_non_moving_space_mem_map_->End(), heap_end); - } size_t heap_capacity = heap_end - heap_begin; // Allocate the card table. @@ -292,6 +271,12 @@ Heap::Heap(size_t initial_size, size_t growth_limit, size_t min_free, size_t max new accounting::RememberedSet("Non-moving space remembered set", this, non_moving_space_); CHECK(non_moving_space_rem_set != nullptr) << "Failed to create non-moving space remembered set"; AddRememberedSet(non_moving_space_rem_set); + if (main_space_ != nullptr && main_space_ != non_moving_space_) { + accounting::RememberedSet* main_space_rem_set = + new accounting::RememberedSet("Main space remembered set", this, main_space_); + CHECK(main_space_rem_set != nullptr) << "Failed to create main space remembered set"; + AddRememberedSet(main_space_rem_set); + } } // TODO: Count objects in the image space here. @@ -329,7 +314,7 @@ Heap::Heap(size_t initial_size, size_t growth_limit, size_t min_free, size_t max } if (kMovingCollector) { // TODO: Clean this up. - bool generational = post_zygote_collector_type_ == kCollectorTypeGSS; + bool generational = foreground_collector_type_ == kCollectorTypeGSS; semi_space_collector_ = new collector::SemiSpace(this, generational, generational ? "generational" : ""); garbage_collectors_.push_back(semi_space_collector_); @@ -347,6 +332,37 @@ Heap::Heap(size_t initial_size, size_t growth_limit, size_t min_free, size_t max } } +void Heap::CreateMainMallocSpace(MemMap* mem_map, size_t initial_size, size_t growth_limit, + size_t capacity) { + // Is background compaction is enabled? + bool can_move_objects = IsMovingGc(background_collector_type_) != + IsMovingGc(foreground_collector_type_); + // If we are the zygote and don't yet have a zygote space, it means that the zygote fork will + // happen in the future. If this happens and we have kCompactZygote enabled we wish to compact + // from the main space to the zygote space. If background compaction is enabled, always pass in + // that we can move objets. + if (kCompactZygote && Runtime::Current()->IsZygote() && !can_move_objects) { + // After the zygote we want this to be false if we don't have background compaction enabled so + // that getting primitive array elements is faster. + can_move_objects = !have_zygote_space_; + } + if (kUseRosAlloc) { + main_space_ = space::RosAllocSpace::CreateFromMemMap(mem_map, "main rosalloc space", + kDefaultStartingSize, initial_size, + growth_limit, capacity, low_memory_mode_, + can_move_objects); + CHECK(main_space_ != nullptr) << "Failed to create rosalloc space"; + } else { + main_space_ = space::DlMallocSpace::CreateFromMemMap(mem_map, "main dlmalloc space", + kDefaultStartingSize, initial_size, + growth_limit, capacity, + can_move_objects); + CHECK(main_space_ != nullptr) << "Failed to create dlmalloc space"; + } + main_space_->SetFootprintLimit(main_space_->Capacity()); + VLOG(heap) << "Created main space " << main_space_; +} + void Heap::ChangeAllocator(AllocatorType allocator) { if (current_allocator_ != allocator) { // These two allocators are only used internally and don't have any entrypoints. @@ -360,13 +376,13 @@ void Heap::ChangeAllocator(AllocatorType allocator) { } void Heap::DisableCompaction() { - if (IsCompactingGC(post_zygote_collector_type_)) { - post_zygote_collector_type_ = kCollectorTypeCMS; + if (IsMovingGc(foreground_collector_type_)) { + foreground_collector_type_ = kCollectorTypeCMS; } - if (IsCompactingGC(background_collector_type_)) { - background_collector_type_ = post_zygote_collector_type_; + if (IsMovingGc(background_collector_type_)) { + background_collector_type_ = foreground_collector_type_; } - TransitionCollector(post_zygote_collector_type_); + TransitionCollector(foreground_collector_type_); } std::string Heap::SafeGetClassDescriptor(mirror::Class* klass) { @@ -428,14 +444,6 @@ void Heap::DumpObject(std::ostream& stream, mirror::Object* obj) { break; } } - if (space == nullptr) { - if (allocator_mem_map_.get() == nullptr || !allocator_mem_map_->HasAddress(obj)) { - stream << "obj " << obj << " not a valid heap address"; - return; - } else if (allocator_mem_map_.get() != nullptr) { - allocator_mem_map_->Protect(PROT_READ | PROT_WRITE); - } - } // Unprotect all the spaces. for (const auto& space : continuous_spaces_) { mprotect(space->Begin(), space->Capacity(), PROT_READ | PROT_WRITE); @@ -478,7 +486,7 @@ void Heap::IncrementDisableMovingGC(Thread* self) { ScopedThreadStateChange tsc(self, kWaitingForGcToComplete); MutexLock mu(self, *gc_complete_lock_); ++disable_moving_gc_count_; - if (IsCompactingGC(collector_type_running_)) { + if (IsMovingGc(collector_type_running_)) { WaitForGcToCompleteLocked(self); } } @@ -496,12 +504,12 @@ void Heap::UpdateProcessState(ProcessState process_state) { // Start at index 1 to avoid "is always false" warning. // Have iteration 1 always transition the collector. TransitionCollector((((i & 1) == 1) == (process_state_ == kProcessStateJankPerceptible)) - ? post_zygote_collector_type_ : background_collector_type_); + ? foreground_collector_type_ : background_collector_type_); usleep(kCollectorTransitionStressWait); } if (process_state_ == kProcessStateJankPerceptible) { // Transition back to foreground right away to prevent jank. - RequestCollectorTransition(post_zygote_collector_type_, 0); + RequestCollectorTransition(foreground_collector_type_, 0); } else { // Don't delay for debug builds since we may want to stress test the GC. RequestCollectorTransition(background_collector_type_, kIsDebugBuild ? 0 : @@ -626,6 +634,10 @@ void Heap::RemoveSpace(space::Space* space) { } if (continuous_space == main_space_) { main_space_ = nullptr; + } else if (continuous_space == bump_pointer_space_) { + bump_pointer_space_ = nullptr; + } else if (continuous_space == temp_space_) { + temp_space_ = nullptr; } } else { DCHECK(space->IsDiscontinuousSpace()); @@ -966,8 +978,10 @@ void Heap::Trim() { managed_reclaimed += alloc_space->Trim(); } } - total_alloc_space_allocated = GetBytesAllocated() - large_object_space_->GetBytesAllocated() - - bump_pointer_space_->Size(); + total_alloc_space_allocated = GetBytesAllocated() - large_object_space_->GetBytesAllocated(); + if (bump_pointer_space_ != nullptr) { + total_alloc_space_allocated -= bump_pointer_space_->Size(); + } const float managed_utilization = static_cast<float>(total_alloc_space_allocated) / static_cast<float>(total_alloc_space_size); uint64_t gc_heap_end_ns = NanoTime(); @@ -1399,7 +1413,7 @@ void Heap::TransitionCollector(CollectorType collector_type) { ScopedThreadStateChange tsc(self, kWaitingPerformingGc); Locks::mutator_lock_->AssertNotHeld(self); const bool copying_transition = - IsCompactingGC(background_collector_type_) || IsCompactingGC(post_zygote_collector_type_); + IsMovingGc(background_collector_type_) || IsMovingGc(foreground_collector_type_); // Busy wait until we can GC (StartGC can fail if we have a non-zero // compacting_gc_disable_count_, this should rarely occurs). for (;;) { @@ -1430,42 +1444,20 @@ void Heap::TransitionCollector(CollectorType collector_type) { case kCollectorTypeSS: // Fall-through. case kCollectorTypeGSS: { - mprotect(temp_space_->Begin(), temp_space_->Capacity(), PROT_READ | PROT_WRITE); - CHECK(main_space_ != nullptr); - Compact(temp_space_, main_space_); - DCHECK(allocator_mem_map_.get() == nullptr); - allocator_mem_map_.reset(main_space_->ReleaseMemMap()); - madvise(main_space_->Begin(), main_space_->Size(), MADV_DONTNEED); - // RemoveSpace does not delete the removed space. - space::Space* old_space = main_space_; - RemoveSpace(old_space); - delete old_space; + if (!IsMovingGc(collector_type_)) { + // We are transitioning from non moving GC -> moving GC, since we copied from the bump + // pointer space last transition it will be protected. + bump_pointer_space_->GetMemMap()->Protect(PROT_READ | PROT_WRITE); + Compact(bump_pointer_space_, main_space_); + } break; } case kCollectorTypeMS: // Fall through. case kCollectorTypeCMS: { - if (IsCompactingGC(collector_type_)) { - // TODO: Use mem-map from temp space? - MemMap* mem_map = allocator_mem_map_.release(); - CHECK(mem_map != nullptr); - size_t starting_size = kDefaultStartingSize; - size_t initial_size = kDefaultInitialSize; - mprotect(mem_map->Begin(), initial_size, PROT_READ | PROT_WRITE); - CHECK(main_space_ == nullptr); - if (kUseRosAlloc) { - main_space_ = - space::RosAllocSpace::CreateFromMemMap(mem_map, "alloc space", starting_size, - initial_size, mem_map->Size(), - mem_map->Size(), low_memory_mode_); - } else { - main_space_ = - space::DlMallocSpace::CreateFromMemMap(mem_map, "alloc space", starting_size, - initial_size, mem_map->Size(), - mem_map->Size()); - } - main_space_->SetFootprintLimit(main_space_->Capacity()); - AddSpace(main_space_); + if (IsMovingGc(collector_type_)) { + // Compact to the main space from the bump pointer space, don't need to swap semispaces. + main_space_->GetMemMap()->Protect(PROT_READ | PROT_WRITE); Compact(main_space_, bump_pointer_space_); } break; @@ -1661,11 +1653,12 @@ void Heap::PreZygoteFork() { VLOG(heap) << "Starting PreZygoteFork"; // Trim the pages at the end of the non moving space. non_moving_space_->Trim(); + // The end of the non-moving space may be protected, unprotect it so that we can copy the zygote + // there. non_moving_space_->GetMemMap()->Protect(PROT_READ | PROT_WRITE); // Change the collector to the post zygote one. - ChangeCollector(post_zygote_collector_type_); - // TODO: Delete bump_pointer_space_ and temp_pointer_space_? - if (semi_space_collector_ != nullptr) { + if (kCompactZygote) { + DCHECK(semi_space_collector_ != nullptr); // Temporarily disable rosalloc verification because the zygote // compaction will mess up the rosalloc internal metadata. ScopedDisableRosAllocVerification disable_rosalloc_verif(this); @@ -1675,18 +1668,47 @@ void Heap::PreZygoteFork() { space::BumpPointerSpace target_space("zygote bump space", non_moving_space_->End(), non_moving_space_->Limit()); // Compact the bump pointer space to a new zygote bump pointer space. - temp_space_->GetMemMap()->Protect(PROT_READ | PROT_WRITE); - zygote_collector.SetFromSpace(bump_pointer_space_); + bool reset_main_space = false; + if (IsMovingGc(collector_type_)) { + zygote_collector.SetFromSpace(bump_pointer_space_); + } else { + CHECK(main_space_ != nullptr); + // Copy from the main space. + zygote_collector.SetFromSpace(main_space_); + reset_main_space = true; + } zygote_collector.SetToSpace(&target_space); + + Runtime::Current()->GetThreadList()->SuspendAll(); zygote_collector.Run(kGcCauseCollectorTransition, false); - CHECK(temp_space_->IsEmpty()); + if (IsMovingGc(collector_type_)) { + SwapSemiSpaces(); + } + Runtime::Current()->GetThreadList()->ResumeAll(); + + if (reset_main_space) { + main_space_->GetMemMap()->Protect(PROT_READ | PROT_WRITE); + madvise(main_space_->Begin(), main_space_->Capacity(), MADV_DONTNEED); + MemMap* mem_map = main_space_->ReleaseMemMap(); + RemoveSpace(main_space_); + delete main_space_; + main_space_ = nullptr; + CreateMainMallocSpace(mem_map, kDefaultInitialSize, mem_map->Size(), mem_map->Size()); + AddSpace(main_space_); + } else { + bump_pointer_space_->GetMemMap()->Protect(PROT_READ | PROT_WRITE); + } + if (temp_space_ != nullptr) { + CHECK(temp_space_->IsEmpty()); + } total_objects_freed_ever_ += semi_space_collector_->GetFreedObjects(); total_bytes_freed_ever_ += semi_space_collector_->GetFreedBytes(); // Update the end and write out image. non_moving_space_->SetEnd(target_space.End()); non_moving_space_->SetLimit(target_space.Limit()); - VLOG(heap) << "Zygote size " << non_moving_space_->Size() << " bytes"; + VLOG(heap) << "Zygote space size " << non_moving_space_->Size() << " bytes"; } + ChangeCollector(foreground_collector_type_); // Save the old space so that we can remove it after we complete creating the zygote space. space::MallocSpace* old_alloc_space = non_moving_space_; // Turn the current alloc space into a zygote space and obtain the new alloc space composed of @@ -1706,18 +1728,12 @@ void Heap::PreZygoteFork() { } space::ZygoteSpace* zygote_space = old_alloc_space->CreateZygoteSpace("alloc space", low_memory_mode_, - &main_space_); + &non_moving_space_); delete old_alloc_space; CHECK(zygote_space != nullptr) << "Failed creating zygote space"; AddSpace(zygote_space, false); - CHECK(main_space_ != nullptr); - if (main_space_->IsRosAllocSpace()) { - rosalloc_space_ = main_space_->AsRosAllocSpace(); - } else if (main_space_->IsDlMallocSpace()) { - dlmalloc_space_ = main_space_->AsDlMallocSpace(); - } - main_space_->SetFootprintLimit(main_space_->Capacity()); - AddSpace(main_space_); + non_moving_space_->SetFootprintLimit(non_moving_space_->Capacity()); + AddSpace(non_moving_space_); have_zygote_space_ = true; // Enable large object space allocations. large_object_threshold_ = kDefaultLargeObjectThreshold; @@ -1727,23 +1743,6 @@ void Heap::PreZygoteFork() { CHECK(mod_union_table != nullptr) << "Failed to create zygote space mod-union table"; AddModUnionTable(mod_union_table); if (collector::SemiSpace::kUseRememberedSet) { - // Add a new remembered set for the new main space. - accounting::RememberedSet* main_space_rem_set = - new accounting::RememberedSet("Main space remembered set", this, main_space_); - CHECK(main_space_rem_set != nullptr) << "Failed to create main space remembered set"; - AddRememberedSet(main_space_rem_set); - } - // Can't use RosAlloc for non moving space due to thread local buffers. - // TODO: Non limited space for non-movable objects? - MemMap* mem_map = post_zygote_non_moving_space_mem_map_.release(); - space::MallocSpace* new_non_moving_space = - space::DlMallocSpace::CreateFromMemMap(mem_map, "Non moving dlmalloc space", kPageSize, - 2 * MB, mem_map->Size(), mem_map->Size()); - AddSpace(new_non_moving_space, false); - CHECK(new_non_moving_space != nullptr) << "Failed to create new non-moving space"; - new_non_moving_space->SetFootprintLimit(new_non_moving_space->Capacity()); - non_moving_space_ = new_non_moving_space; - if (collector::SemiSpace::kUseRememberedSet) { // Add a new remembered set for the post-zygote non-moving space. accounting::RememberedSet* post_zygote_non_moving_space_rem_set = new accounting::RememberedSet("Post-zygote non-moving space remembered set", this, @@ -1781,9 +1780,9 @@ void Heap::MarkAllocStack(accounting::SpaceBitmap* bitmap1, } void Heap::SwapSemiSpaces() { - // Swap the spaces so we allocate into the space which we just evacuated. + CHECK(bump_pointer_space_ != nullptr); + CHECK(temp_space_ != nullptr); std::swap(bump_pointer_space_, temp_space_); - bump_pointer_space_->Clear(); } void Heap::Compact(space::ContinuousMemMapAllocSpace* target_space, @@ -1826,7 +1825,7 @@ collector::GcType Heap::CollectGarbageInternal(collector::GcType gc_type, GcCaus MutexLock mu(self, *gc_complete_lock_); // Ensure there is only one GC at a time. WaitForGcToCompleteLocked(self); - compacting_gc = IsCompactingGC(collector_type_); + compacting_gc = IsMovingGc(collector_type_); // GC can be disabled if someone has a used GetPrimitiveArrayCritical. if (compacting_gc && disable_moving_gc_count_ != 0) { LOG(WARNING) << "Skipping GC due to disable moving GC count " << disable_moving_gc_count_; @@ -1881,7 +1880,14 @@ collector::GcType Heap::CollectGarbageInternal(collector::GcType gc_type, GcCaus << "Could not find garbage collector with collector_type=" << static_cast<size_t>(collector_type_) << " and gc_type=" << gc_type; ATRACE_BEGIN(StringPrintf("%s %s GC", PrettyCause(gc_cause), collector->GetName()).c_str()); - collector->Run(gc_cause, clear_soft_references || runtime->IsZygote()); + if (compacting_gc) { + runtime->GetThreadList()->SuspendAll(); + collector->Run(gc_cause, clear_soft_references || runtime->IsZygote()); + SwapSemiSpaces(); + runtime->GetThreadList()->ResumeAll(); + } else { + collector->Run(gc_cause, clear_soft_references || runtime->IsZygote()); + } total_objects_freed_ever_ += collector->GetFreedObjects(); total_bytes_freed_ever_ += collector->GetFreedBytes(); RequestHeapTrim(); @@ -2484,29 +2490,15 @@ void Heap::SetIdealFootprint(size_t max_allowed_footprint) { bool Heap::IsMovableObject(const mirror::Object* obj) const { if (kMovingCollector) { - DCHECK(!IsInTempSpace(obj)); - if (bump_pointer_space_->HasAddress(obj)) { - return true; - } - // TODO: Refactor this logic into the space itself? - // Objects in the main space are only copied during background -> foreground transitions or - // visa versa. - if (main_space_ != nullptr && main_space_->HasAddress(obj) && - (IsCompactingGC(background_collector_type_) || - IsCompactingGC(post_zygote_collector_type_))) { - return true; + space::Space* space = FindContinuousSpaceFromObject(obj, true); + if (space != nullptr) { + // TODO: Check large object? + return space->CanMoveObjects(); } } return false; } -bool Heap::IsInTempSpace(const mirror::Object* obj) const { - if (temp_space_->HasAddress(obj) && !temp_space_->Contains(obj)) { - return true; - } - return false; -} - void Heap::UpdateMaxNativeFootprint() { size_t native_size = native_bytes_allocated_; // TODO: Tune the native heap utilization to be a value other than the java heap utilization. diff --git a/runtime/gc/heap.h b/runtime/gc/heap.h index a8989ec..912cf7d 100644 --- a/runtime/gc/heap.h +++ b/runtime/gc/heap.h @@ -150,7 +150,7 @@ class Heap { explicit Heap(size_t initial_size, size_t growth_limit, size_t min_free, size_t max_free, double target_utilization, size_t capacity, const std::string& original_image_file_name, - CollectorType post_zygote_collector_type, CollectorType background_collector_type, + CollectorType foreground_collector_type, CollectorType background_collector_type, size_t parallel_gc_threads, size_t conc_gc_threads, bool low_memory_mode, size_t long_pause_threshold, size_t long_gc_threshold, bool ignore_max_footprint, bool use_tlab, bool verify_pre_gc_heap, @@ -196,8 +196,6 @@ class Heap { void VisitObjects(ObjectCallback callback, void* arg) SHARED_LOCKS_REQUIRED(Locks::heap_bitmap_lock_, Locks::mutator_lock_); - void SwapSemiSpaces() EXCLUSIVE_LOCKS_REQUIRED(Locks::mutator_lock_); - void CheckPreconditionsForAllocObject(mirror::Class* c, size_t byte_count) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); void ThrowOutOfMemoryError(size_t byte_count, bool large_object_allocation); @@ -249,10 +247,6 @@ class Heap { // Returns true if there is any chance that the object (obj) will move. bool IsMovableObject(const mirror::Object* obj) const; - // Returns true if an object is in the temp space, if this happens its usually indicative of - // compaction related errors. - bool IsInTempSpace(const mirror::Object* obj) const; - // Enables us to compacting GC until objects are released. void IncrementDisableMovingGC(Thread* self); void DecrementDisableMovingGC(Thread* self); @@ -568,7 +562,8 @@ class Heap { private: void Compact(space::ContinuousMemMapAllocSpace* target_space, - space::ContinuousMemMapAllocSpace* source_space); + space::ContinuousMemMapAllocSpace* source_space) + EXCLUSIVE_LOCKS_REQUIRED(Locks::mutator_lock_); void FinishGC(Thread* self, collector::GcType gc_type) LOCKS_EXCLUDED(gc_complete_lock_); @@ -580,7 +575,7 @@ class Heap { static ALWAYS_INLINE bool AllocatorMayHaveConcurrentGC(AllocatorType allocator_type) { return AllocatorHasAllocationStack(allocator_type); } - static bool IsCompactingGC(CollectorType collector_type) { + static bool IsMovingGc(CollectorType collector_type) { return collector_type == kCollectorTypeSS || collector_type == kCollectorTypeGSS || collector_type == kCollectorTypeCC; } @@ -609,6 +604,10 @@ class Heap { size_t bytes) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + // Need to do this with mutators paused so that somebody doesn't accidentally allocate into the + // wrong space. + void SwapSemiSpaces() EXCLUSIVE_LOCKS_REQUIRED(Locks::mutator_lock_); + // Try to allocate a number of bytes, this function never does any GCs. Needs to be inlined so // that the switch statement is constant optimized in the entrypoints. template <const bool kInstrumented, const bool kGrow> @@ -668,6 +667,10 @@ class Heap { // Find a collector based on GC type. collector::GarbageCollector* FindCollectorByGcType(collector::GcType gc_type); + // Create the main free list space, typically either a RosAlloc space or DlMalloc space. + void CreateMainMallocSpace(MemMap* mem_map, size_t initial_size, size_t growth_limit, + size_t capacity); + // Given the current contents of the alloc space, increase the allowed heap footprint to match // the target utilization ratio. This should only be called immediately after a full garbage // collection. @@ -737,17 +740,10 @@ class Heap { // A remembered set remembers all of the references from the it's space to the target space. SafeMap<space::Space*, accounting::RememberedSet*> remembered_sets_; - // Keep the free list allocator mem map lying around when we transition to background so that we - // don't have to worry about virtual address space fragmentation. - UniquePtr<MemMap> allocator_mem_map_; - - // The mem-map which we will use for the non-moving space after the zygote is done forking: - UniquePtr<MemMap> post_zygote_non_moving_space_mem_map_; - // The current collector type. CollectorType collector_type_; - // Which collector we will switch to after zygote fork. - CollectorType post_zygote_collector_type_; + // Which collector we use when the app is in the foreground. + CollectorType foreground_collector_type_; // Which collector we will use when the app is notified of a transition to background. CollectorType background_collector_type_; // Desired collector type, heap trimming daemon transitions the heap if it is != collector_type_. diff --git a/runtime/gc/space/bump_pointer_space.cc b/runtime/gc/space/bump_pointer_space.cc index a955cc8..6bd0526 100644 --- a/runtime/gc/space/bump_pointer_space.cc +++ b/runtime/gc/space/bump_pointer_space.cc @@ -38,6 +38,10 @@ BumpPointerSpace* BumpPointerSpace::Create(const std::string& name, size_t capac return new BumpPointerSpace(name, mem_map.release()); } +BumpPointerSpace* BumpPointerSpace::CreateFromMemMap(const std::string& name, MemMap* mem_map) { + return new BumpPointerSpace(name, mem_map); +} + BumpPointerSpace::BumpPointerSpace(const std::string& name, byte* begin, byte* limit) : ContinuousMemMapAllocSpace(name, nullptr, begin, begin, limit, kGcRetentionPolicyAlwaysCollect), @@ -61,9 +65,6 @@ BumpPointerSpace::BumpPointerSpace(const std::string& name, MemMap* mem_map) void BumpPointerSpace::Clear() { // Release the pages back to the operating system. CHECK_NE(madvise(Begin(), Limit() - Begin(), MADV_DONTNEED), -1) << "madvise failed"; -} - -void BumpPointerSpace::Reset() { // Reset the end of the space back to the beginning, we move the end forward as we allocate // objects. SetEnd(Begin()); diff --git a/runtime/gc/space/bump_pointer_space.h b/runtime/gc/space/bump_pointer_space.h index 3ab5df4..ecfeae5 100644 --- a/runtime/gc/space/bump_pointer_space.h +++ b/runtime/gc/space/bump_pointer_space.h @@ -43,6 +43,7 @@ class BumpPointerSpace FINAL : public ContinuousMemMapAllocSpace { // guaranteed to be granted, if it is required, the caller should call Begin on the returned // space to confirm the request was granted. static BumpPointerSpace* Create(const std::string& name, size_t capacity, byte* requested_begin); + static BumpPointerSpace* CreateFromMemMap(const std::string& name, MemMap* mem_map); // Allocate num_bytes, returns nullptr if the space is full. mirror::Object* Alloc(Thread* self, size_t num_bytes, size_t* bytes_allocated, @@ -92,11 +93,8 @@ class BumpPointerSpace FINAL : public ContinuousMemMapAllocSpace { return nullptr; } - // Madvise the memory back to the OS. - void Clear() OVERRIDE; - - // Reset the pointer to the start of the space. - void Reset() OVERRIDE LOCKS_EXCLUDED(block_lock_); + // Reset the space to empty. + void Clear() OVERRIDE LOCKS_EXCLUDED(block_lock_); void Dump(std::ostream& os) const; @@ -113,6 +111,9 @@ class BumpPointerSpace FINAL : public ContinuousMemMapAllocSpace { return Begin() == End(); } + bool CanMoveObjects() const OVERRIDE { + return true; + } bool Contains(const mirror::Object* obj) const { const byte* byte_obj = reinterpret_cast<const byte*>(obj); diff --git a/runtime/gc/space/dlmalloc_space-inl.h b/runtime/gc/space/dlmalloc_space-inl.h index 02d8b54..4c8a35e 100644 --- a/runtime/gc/space/dlmalloc_space-inl.h +++ b/runtime/gc/space/dlmalloc_space-inl.h @@ -52,7 +52,7 @@ inline size_t DlMallocSpace::AllocationSizeNonvirtual(mirror::Object* obj, size_ inline mirror::Object* DlMallocSpace::AllocWithoutGrowthLocked(Thread* /*self*/, size_t num_bytes, size_t* bytes_allocated, size_t* usable_size) { - mirror::Object* result = reinterpret_cast<mirror::Object*>(mspace_malloc(mspace_for_alloc_, num_bytes)); + mirror::Object* result = reinterpret_cast<mirror::Object*>(mspace_malloc(mspace_, num_bytes)); if (LIKELY(result != NULL)) { if (kDebugSpaces) { CHECK(Contains(result)) << "Allocation (" << reinterpret_cast<void*>(result) diff --git a/runtime/gc/space/dlmalloc_space.cc b/runtime/gc/space/dlmalloc_space.cc index 60f566c..be88b33 100644 --- a/runtime/gc/space/dlmalloc_space.cc +++ b/runtime/gc/space/dlmalloc_space.cc @@ -36,15 +36,19 @@ static constexpr bool kPrefetchDuringDlMallocFreeList = true; template class ValgrindMallocSpace<DlMallocSpace, void*>; DlMallocSpace::DlMallocSpace(const std::string& name, MemMap* mem_map, void* mspace, byte* begin, - byte* end, byte* limit, size_t growth_limit) - : MallocSpace(name, mem_map, begin, end, limit, growth_limit), - mspace_(mspace), mspace_for_alloc_(mspace) { + byte* end, byte* limit, size_t growth_limit, + bool can_move_objects, size_t starting_size, + size_t initial_size) + : MallocSpace(name, mem_map, begin, end, limit, growth_limit, true, can_move_objects, + starting_size, initial_size), + mspace_(mspace) { CHECK(mspace != NULL); } DlMallocSpace* DlMallocSpace::CreateFromMemMap(MemMap* mem_map, const std::string& name, size_t starting_size, size_t initial_size, - size_t growth_limit, size_t capacity) { + size_t growth_limit, size_t capacity, + bool can_move_objects) { DCHECK(mem_map != nullptr); void* mspace = CreateMspace(mem_map->Begin(), starting_size, initial_size); if (mspace == nullptr) { @@ -62,14 +66,17 @@ DlMallocSpace* DlMallocSpace::CreateFromMemMap(MemMap* mem_map, const std::strin byte* begin = mem_map->Begin(); if (Runtime::Current()->RunningOnValgrind()) { return new ValgrindMallocSpace<DlMallocSpace, void*>( - name, mem_map, mspace, begin, end, begin + capacity, growth_limit, initial_size); + name, mem_map, mspace, begin, end, begin + capacity, growth_limit, initial_size, + can_move_objects, starting_size); } else { - return new DlMallocSpace(name, mem_map, mspace, begin, end, begin + capacity, growth_limit); + return new DlMallocSpace(name, mem_map, mspace, begin, end, begin + capacity, growth_limit, + can_move_objects, starting_size, initial_size); } } -DlMallocSpace* DlMallocSpace::Create(const std::string& name, size_t initial_size, size_t growth_limit, - size_t capacity, byte* requested_begin) { +DlMallocSpace* DlMallocSpace::Create(const std::string& name, size_t initial_size, + size_t growth_limit, size_t capacity, byte* requested_begin, + bool can_move_objects) { uint64_t start_time = 0; if (VLOG_IS_ON(heap) || VLOG_IS_ON(startup)) { start_time = NanoTime(); @@ -93,7 +100,7 @@ DlMallocSpace* DlMallocSpace::Create(const std::string& name, size_t initial_siz return nullptr; } DlMallocSpace* space = CreateFromMemMap(mem_map, name, starting_size, initial_size, - growth_limit, capacity); + growth_limit, capacity, can_move_objects); // We start out with only the initial size possibly containing objects. if (VLOG_IS_ON(heap) || VLOG_IS_ON(startup)) { LOG(INFO) << "DlMallocSpace::Create exiting (" << PrettyDuration(NanoTime() - start_time) @@ -143,8 +150,10 @@ mirror::Object* DlMallocSpace::AllocWithGrowth(Thread* self, size_t num_bytes, MallocSpace* DlMallocSpace::CreateInstance(const std::string& name, MemMap* mem_map, void* allocator, byte* begin, byte* end, - byte* limit, size_t growth_limit) { - return new DlMallocSpace(name, mem_map, allocator, begin, end, limit, growth_limit); + byte* limit, size_t growth_limit, + bool can_move_objects) { + return new DlMallocSpace(name, mem_map, allocator, begin, end, limit, growth_limit, + can_move_objects, starting_size_, initial_size_); } size_t DlMallocSpace::Free(Thread* self, mirror::Object* ptr) { @@ -280,13 +289,13 @@ uint64_t DlMallocSpace::GetObjectsAllocated() { } void DlMallocSpace::Clear() { + size_t footprint_limit = GetFootprintLimit(); madvise(GetMemMap()->Begin(), GetMemMap()->Size(), MADV_DONTNEED); - GetLiveBitmap()->Clear(); - GetMarkBitmap()->Clear(); -} - -void DlMallocSpace::Reset() { - // TODO: Delete and create new mspace here. + live_bitmap_->Clear(); + mark_bitmap_->Clear(); + end_ = Begin() + starting_size_; + mspace_ = CreateMspace(mem_map_->Begin(), starting_size_, initial_size_); + SetFootprintLimit(footprint_limit); } #ifndef NDEBUG diff --git a/runtime/gc/space/dlmalloc_space.h b/runtime/gc/space/dlmalloc_space.h index 76c4489..accd26b 100644 --- a/runtime/gc/space/dlmalloc_space.h +++ b/runtime/gc/space/dlmalloc_space.h @@ -36,14 +36,15 @@ class DlMallocSpace : public MallocSpace { // Create a DlMallocSpace from an existing mem_map. static DlMallocSpace* CreateFromMemMap(MemMap* mem_map, const std::string& name, size_t starting_size, size_t initial_size, - size_t growth_limit, size_t capacity); + size_t growth_limit, size_t capacity, + bool can_move_objects); // Create a DlMallocSpace with the requested sizes. The requested // base address is not guaranteed to be granted, if it is required, // the caller should call Begin on the returned space to confirm the // request was granted. static DlMallocSpace* Create(const std::string& name, size_t initial_size, size_t growth_limit, - size_t capacity, byte* requested_begin); + size_t capacity, byte* requested_begin, bool can_move_objects); // Virtual to allow ValgrindMallocSpace to intercept. virtual mirror::Object* AllocWithGrowth(Thread* self, size_t num_bytes, size_t* bytes_allocated, @@ -107,13 +108,13 @@ class DlMallocSpace : public MallocSpace { void SetFootprintLimit(size_t limit) OVERRIDE; MallocSpace* CreateInstance(const std::string& name, MemMap* mem_map, void* allocator, - byte* begin, byte* end, byte* limit, size_t growth_limit); + byte* begin, byte* end, byte* limit, size_t growth_limit, + bool can_move_objects); uint64_t GetBytesAllocated() OVERRIDE; uint64_t GetObjectsAllocated() OVERRIDE; - void Clear() OVERRIDE; - void Reset() OVERRIDE; + virtual void Clear() OVERRIDE; bool IsDlMallocSpace() const OVERRIDE { return true; @@ -125,7 +126,8 @@ class DlMallocSpace : public MallocSpace { protected: DlMallocSpace(const std::string& name, MemMap* mem_map, void* mspace, byte* begin, byte* end, - byte* limit, size_t growth_limit); + byte* limit, size_t growth_limit, bool can_move_objects, size_t starting_size, + size_t initial_size); private: mirror::Object* AllocWithoutGrowthLocked(Thread* self, size_t num_bytes, size_t* bytes_allocated, @@ -142,11 +144,7 @@ class DlMallocSpace : public MallocSpace { static const size_t kChunkOverhead = kWordSize; // Underlying malloc space. - void* const mspace_; - - // An mspace pointer used for allocation. Equals mspace_ or nullptr after InvalidateAllocator() - // is called. - void* mspace_for_alloc_; + void* mspace_; friend class collector::MarkSweep; diff --git a/runtime/gc/space/dlmalloc_space_base_test.cc b/runtime/gc/space/dlmalloc_space_base_test.cc index 508d869..129eace 100644 --- a/runtime/gc/space/dlmalloc_space_base_test.cc +++ b/runtime/gc/space/dlmalloc_space_base_test.cc @@ -23,7 +23,7 @@ namespace space { MallocSpace* CreateDlMallocSpace(const std::string& name, size_t initial_size, size_t growth_limit, size_t capacity, byte* requested_begin) { - return DlMallocSpace::Create(name, initial_size, growth_limit, capacity, requested_begin); + return DlMallocSpace::Create(name, initial_size, growth_limit, capacity, requested_begin, false); } TEST_SPACE_CREATE_FN_BASE(DlMallocSpace, CreateDlMallocSpace) diff --git a/runtime/gc/space/dlmalloc_space_random_test.cc b/runtime/gc/space/dlmalloc_space_random_test.cc index 43a1bf0..c4f8bae 100644 --- a/runtime/gc/space/dlmalloc_space_random_test.cc +++ b/runtime/gc/space/dlmalloc_space_random_test.cc @@ -23,7 +23,7 @@ namespace space { MallocSpace* CreateDlMallocSpace(const std::string& name, size_t initial_size, size_t growth_limit, size_t capacity, byte* requested_begin) { - return DlMallocSpace::Create(name, initial_size, growth_limit, capacity, requested_begin); + return DlMallocSpace::Create(name, initial_size, growth_limit, capacity, requested_begin, false); } TEST_SPACE_CREATE_FN_RANDOM(DlMallocSpace, CreateDlMallocSpace) diff --git a/runtime/gc/space/dlmalloc_space_static_test.cc b/runtime/gc/space/dlmalloc_space_static_test.cc index 4fbc81e..edaa198 100644 --- a/runtime/gc/space/dlmalloc_space_static_test.cc +++ b/runtime/gc/space/dlmalloc_space_static_test.cc @@ -23,7 +23,7 @@ namespace space { MallocSpace* CreateDlMallocSpace(const std::string& name, size_t initial_size, size_t growth_limit, size_t capacity, byte* requested_begin) { - return DlMallocSpace::Create(name, initial_size, growth_limit, capacity, requested_begin); + return DlMallocSpace::Create(name, initial_size, growth_limit, capacity, requested_begin, false); } TEST_SPACE_CREATE_FN_STATIC(DlMallocSpace, CreateDlMallocSpace) diff --git a/runtime/gc/space/image_space.h b/runtime/gc/space/image_space.h index 116c498..6b63d10 100644 --- a/runtime/gc/space/image_space.h +++ b/runtime/gc/space/image_space.h @@ -75,6 +75,10 @@ class ImageSpace : public MemMapSpace { void Sweep(bool /* swap_bitmaps */, size_t* /* freed_objects */, size_t* /* freed_bytes */) { } + bool CanMoveObjects() const OVERRIDE { + return false; + } + private: // Tries to initialize an ImageSpace from the given image path, // returning NULL on error. diff --git a/runtime/gc/space/large_object_space.h b/runtime/gc/space/large_object_space.h index eb01325..18e518f 100644 --- a/runtime/gc/space/large_object_space.h +++ b/runtime/gc/space/large_object_space.h @@ -75,6 +75,10 @@ class LargeObjectSpace : public DiscontinuousSpace, public AllocSpace { void Sweep(bool swap_bitmaps, size_t* freed_objects, size_t* freed_bytes); + virtual bool CanMoveObjects() const OVERRIDE { + return false; + } + protected: explicit LargeObjectSpace(const std::string& name); diff --git a/runtime/gc/space/malloc_space.cc b/runtime/gc/space/malloc_space.cc index dac043e..c3ca096 100644 --- a/runtime/gc/space/malloc_space.cc +++ b/runtime/gc/space/malloc_space.cc @@ -37,10 +37,12 @@ size_t MallocSpace::bitmap_index_ = 0; MallocSpace::MallocSpace(const std::string& name, MemMap* mem_map, byte* begin, byte* end, byte* limit, size_t growth_limit, - bool create_bitmaps) + bool create_bitmaps, bool can_move_objects, size_t starting_size, + size_t initial_size) : ContinuousMemMapAllocSpace(name, mem_map, begin, end, limit, kGcRetentionPolicyAlwaysCollect), recent_free_pos_(0), lock_("allocation space lock", kAllocSpaceLock), - growth_limit_(growth_limit) { + growth_limit_(growth_limit), can_move_objects_(can_move_objects), + starting_size_(starting_size), initial_size_(initial_size) { if (create_bitmaps) { size_t bitmap_index = bitmap_index_++; static const uintptr_t kGcCardSize = static_cast<uintptr_t>(accounting::CardTable::kCardSize); @@ -201,7 +203,7 @@ ZygoteSpace* MallocSpace::CreateZygoteSpace(const char* alloc_space_name, bool l CHECK_MEMORY_CALL(mprotect, (end, capacity - initial_size, PROT_NONE), alloc_space_name); } *out_malloc_space = CreateInstance(alloc_space_name, mem_map.release(), allocator, end_, end, - limit_, growth_limit); + limit_, growth_limit, CanMoveObjects()); SetLimit(End()); live_bitmap_->SetHeapLimit(reinterpret_cast<uintptr_t>(End())); CHECK_EQ(live_bitmap_->HeapLimit(), reinterpret_cast<uintptr_t>(End())); diff --git a/runtime/gc/space/malloc_space.h b/runtime/gc/space/malloc_space.h index fbcee5f..dd4e5d4 100644 --- a/runtime/gc/space/malloc_space.h +++ b/runtime/gc/space/malloc_space.h @@ -114,7 +114,8 @@ class MallocSpace : public ContinuousMemMapAllocSpace { void SetGrowthLimit(size_t growth_limit); virtual MallocSpace* CreateInstance(const std::string& name, MemMap* mem_map, void* allocator, - byte* begin, byte* end, byte* limit, size_t growth_limit) = 0; + byte* begin, byte* end, byte* limit, size_t growth_limit, + bool can_move_objects) = 0; // Splits ourself into a zygote space and new malloc space which has our unused memory. When true, // the low memory mode argument specifies that the heap wishes the created space to be more @@ -127,9 +128,14 @@ class MallocSpace : public ContinuousMemMapAllocSpace { // Returns the class of a recently freed object. mirror::Class* FindRecentFreedObject(const mirror::Object* obj); + bool CanMoveObjects() const OVERRIDE { + return can_move_objects_; + } + protected: MallocSpace(const std::string& name, MemMap* mem_map, byte* begin, byte* end, - byte* limit, size_t growth_limit, bool create_bitmaps = true); + byte* limit, size_t growth_limit, bool create_bitmaps, bool can_move_objects, + size_t starting_size, size_t initial_size); static MemMap* CreateMemMap(const std::string& name, size_t starting_size, size_t* initial_size, size_t* growth_limit, size_t* capacity, byte* requested_begin); @@ -167,6 +173,13 @@ class MallocSpace : public ContinuousMemMapAllocSpace { // one time by a call to ClearGrowthLimit. size_t growth_limit_; + // True if objects in the space are movable. + const bool can_move_objects_; + + // Starting and initial sized, used when you reset the space. + const size_t starting_size_; + const size_t initial_size_; + private: static void SweepCallback(size_t num_ptrs, mirror::Object** ptrs, void* arg) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); diff --git a/runtime/gc/space/rosalloc_space.cc b/runtime/gc/space/rosalloc_space.cc index 5c5e7f8..afac2a2 100644 --- a/runtime/gc/space/rosalloc_space.cc +++ b/runtime/gc/space/rosalloc_space.cc @@ -34,19 +34,23 @@ namespace space { static constexpr bool kPrefetchDuringRosAllocFreeList = true; -template class ValgrindMallocSpace<RosAllocSpace, allocator::RosAlloc*>; +// TODO: Fix +// template class ValgrindMallocSpace<RosAllocSpace, allocator::RosAlloc*>; RosAllocSpace::RosAllocSpace(const std::string& name, MemMap* mem_map, art::gc::allocator::RosAlloc* rosalloc, byte* begin, byte* end, - byte* limit, size_t growth_limit) - : MallocSpace(name, mem_map, begin, end, limit, growth_limit), rosalloc_(rosalloc) { - CHECK(rosalloc != NULL); + byte* limit, size_t growth_limit, bool can_move_objects, + size_t starting_size, size_t initial_size, bool low_memory_mode) + : MallocSpace(name, mem_map, begin, end, limit, growth_limit, true, can_move_objects, + starting_size, initial_size), + rosalloc_(rosalloc), low_memory_mode_(low_memory_mode) { + CHECK(rosalloc != nullptr); } RosAllocSpace* RosAllocSpace::CreateFromMemMap(MemMap* mem_map, const std::string& name, size_t starting_size, size_t initial_size, size_t growth_limit, size_t capacity, - bool low_memory_mode) { + bool low_memory_mode, bool can_move_objects) { DCHECK(mem_map != nullptr); allocator::RosAlloc* rosalloc = CreateRosAlloc(mem_map->Begin(), starting_size, initial_size, capacity, low_memory_mode); @@ -66,10 +70,10 @@ RosAllocSpace* RosAllocSpace::CreateFromMemMap(MemMap* mem_map, const std::strin // TODO: Fix RosAllocSpace to support valgrind. There is currently some issues with // AllocationSize caused by redzones. b/12944686 if (false && Runtime::Current()->GetHeap()->RunningOnValgrind()) { - return new ValgrindMallocSpace<RosAllocSpace, allocator::RosAlloc*>( - name, mem_map, rosalloc, begin, end, begin + capacity, growth_limit, initial_size); + LOG(FATAL) << "Unimplemented"; } else { - return new RosAllocSpace(name, mem_map, rosalloc, begin, end, begin + capacity, growth_limit); + return new RosAllocSpace(name, mem_map, rosalloc, begin, end, begin + capacity, growth_limit, + can_move_objects, starting_size, initial_size, low_memory_mode); } } @@ -79,7 +83,7 @@ RosAllocSpace::~RosAllocSpace() { RosAllocSpace* RosAllocSpace::Create(const std::string& name, size_t initial_size, size_t growth_limit, size_t capacity, byte* requested_begin, - bool low_memory_mode) { + bool low_memory_mode, bool can_move_objects) { uint64_t start_time = 0; if (VLOG_IS_ON(heap) || VLOG_IS_ON(startup)) { start_time = NanoTime(); @@ -104,7 +108,8 @@ RosAllocSpace* RosAllocSpace::Create(const std::string& name, size_t initial_siz } RosAllocSpace* space = CreateFromMemMap(mem_map, name, starting_size, initial_size, - growth_limit, capacity, low_memory_mode); + growth_limit, capacity, low_memory_mode, + can_move_objects); // We start out with only the initial size possibly containing objects. if (VLOG_IS_ON(heap) || VLOG_IS_ON(startup)) { LOG(INFO) << "RosAllocSpace::Create exiting (" << PrettyDuration(NanoTime() - start_time) @@ -113,7 +118,8 @@ RosAllocSpace* RosAllocSpace::Create(const std::string& name, size_t initial_siz return space; } -allocator::RosAlloc* RosAllocSpace::CreateRosAlloc(void* begin, size_t morecore_start, size_t initial_size, +allocator::RosAlloc* RosAllocSpace::CreateRosAlloc(void* begin, size_t morecore_start, + size_t initial_size, size_t maximum_size, bool low_memory_mode) { // clear errno to allow PLOG on error errno = 0; @@ -154,9 +160,11 @@ mirror::Object* RosAllocSpace::AllocWithGrowth(Thread* self, size_t num_bytes, } MallocSpace* RosAllocSpace::CreateInstance(const std::string& name, MemMap* mem_map, void* allocator, - byte* begin, byte* end, byte* limit, size_t growth_limit) { + byte* begin, byte* end, byte* limit, size_t growth_limit, + bool can_move_objects) { return new RosAllocSpace(name, mem_map, reinterpret_cast<allocator::RosAlloc*>(allocator), - begin, end, limit, growth_limit); + begin, end, limit, growth_limit, can_move_objects, starting_size_, + initial_size_, low_memory_mode_); } size_t RosAllocSpace::Free(Thread* self, mirror::Object* ptr) { @@ -333,13 +341,15 @@ void RosAllocSpace::AssertAllThreadLocalBuffersAreRevoked() { } void RosAllocSpace::Clear() { + size_t footprint_limit = GetFootprintLimit(); madvise(GetMemMap()->Begin(), GetMemMap()->Size(), MADV_DONTNEED); - GetLiveBitmap()->Clear(); - GetMarkBitmap()->Clear(); -} - -void RosAllocSpace::Reset() { - // TODO: Delete and create new mspace here. + live_bitmap_->Clear(); + mark_bitmap_->Clear(); + end_ = begin_ + starting_size_; + delete rosalloc_; + rosalloc_ = CreateRosAlloc(mem_map_->Begin(), starting_size_, initial_size_, Capacity(), + low_memory_mode_); + SetFootprintLimit(footprint_limit); } } // namespace space diff --git a/runtime/gc/space/rosalloc_space.h b/runtime/gc/space/rosalloc_space.h index 900e7a9..a156738 100644 --- a/runtime/gc/space/rosalloc_space.h +++ b/runtime/gc/space/rosalloc_space.h @@ -39,11 +39,12 @@ class RosAllocSpace : public MallocSpace { // the caller should call Begin on the returned space to confirm the // request was granted. static RosAllocSpace* Create(const std::string& name, size_t initial_size, size_t growth_limit, - size_t capacity, byte* requested_begin, bool low_memory_mode); + size_t capacity, byte* requested_begin, bool low_memory_mode, + bool can_move_objects); static RosAllocSpace* CreateFromMemMap(MemMap* mem_map, const std::string& name, size_t starting_size, size_t initial_size, size_t growth_limit, size_t capacity, - bool low_memory_mode); + bool low_memory_mode, bool can_move_objects); mirror::Object* AllocWithGrowth(Thread* self, size_t num_bytes, size_t* bytes_allocated, size_t* usable_size) OVERRIDE LOCKS_EXCLUDED(lock_); @@ -80,9 +81,10 @@ class RosAllocSpace : public MallocSpace { void SetFootprintLimit(size_t limit) OVERRIDE; void Clear() OVERRIDE; - void Reset() OVERRIDE; + MallocSpace* CreateInstance(const std::string& name, MemMap* mem_map, void* allocator, - byte* begin, byte* end, byte* limit, size_t growth_limit); + byte* begin, byte* end, byte* limit, size_t growth_limit, + bool can_move_objects) OVERRIDE; uint64_t GetBytesAllocated() OVERRIDE; uint64_t GetObjectsAllocated() OVERRIDE; @@ -110,7 +112,8 @@ class RosAllocSpace : public MallocSpace { protected: RosAllocSpace(const std::string& name, MemMap* mem_map, allocator::RosAlloc* rosalloc, - byte* begin, byte* end, byte* limit, size_t growth_limit); + byte* begin, byte* end, byte* limit, size_t growth_limit, bool can_move_objects, + size_t starting_size, size_t initial_size, bool low_memory_mode); private: mirror::Object* AllocCommon(Thread* self, size_t num_bytes, size_t* bytes_allocated, @@ -132,7 +135,9 @@ class RosAllocSpace : public MallocSpace { LOCKS_EXCLUDED(Locks::runtime_shutdown_lock_, Locks::thread_list_lock_); // Underlying rosalloc. - allocator::RosAlloc* const rosalloc_; + allocator::RosAlloc* rosalloc_; + + const bool low_memory_mode_; friend class collector::MarkSweep; diff --git a/runtime/gc/space/rosalloc_space_base_test.cc b/runtime/gc/space/rosalloc_space_base_test.cc index df42076..c3157fa 100644 --- a/runtime/gc/space/rosalloc_space_base_test.cc +++ b/runtime/gc/space/rosalloc_space_base_test.cc @@ -23,7 +23,7 @@ namespace space { MallocSpace* CreateRosAllocSpace(const std::string& name, size_t initial_size, size_t growth_limit, size_t capacity, byte* requested_begin) { return RosAllocSpace::Create(name, initial_size, growth_limit, capacity, requested_begin, - Runtime::Current()->GetHeap()->IsLowMemoryMode()); + Runtime::Current()->GetHeap()->IsLowMemoryMode(), false); } TEST_SPACE_CREATE_FN_BASE(RosAllocSpace, CreateRosAllocSpace) diff --git a/runtime/gc/space/rosalloc_space_random_test.cc b/runtime/gc/space/rosalloc_space_random_test.cc index 4d37c9e..864bbc9 100644 --- a/runtime/gc/space/rosalloc_space_random_test.cc +++ b/runtime/gc/space/rosalloc_space_random_test.cc @@ -23,7 +23,7 @@ namespace space { MallocSpace* CreateRosAllocSpace(const std::string& name, size_t initial_size, size_t growth_limit, size_t capacity, byte* requested_begin) { return RosAllocSpace::Create(name, initial_size, growth_limit, capacity, requested_begin, - Runtime::Current()->GetHeap()->IsLowMemoryMode()); + Runtime::Current()->GetHeap()->IsLowMemoryMode(), false); } TEST_SPACE_CREATE_FN_RANDOM(RosAllocSpace, CreateRosAllocSpace) diff --git a/runtime/gc/space/rosalloc_space_static_test.cc b/runtime/gc/space/rosalloc_space_static_test.cc index 9f11fd0..c0e2ac8 100644 --- a/runtime/gc/space/rosalloc_space_static_test.cc +++ b/runtime/gc/space/rosalloc_space_static_test.cc @@ -23,7 +23,7 @@ namespace space { MallocSpace* CreateRosAllocSpace(const std::string& name, size_t initial_size, size_t growth_limit, size_t capacity, byte* requested_begin) { return RosAllocSpace::Create(name, initial_size, growth_limit, capacity, requested_begin, - Runtime::Current()->GetHeap()->IsLowMemoryMode()); + Runtime::Current()->GetHeap()->IsLowMemoryMode(), false); } TEST_SPACE_CREATE_FN_STATIC(RosAllocSpace, CreateRosAllocSpace) diff --git a/runtime/gc/space/space.h b/runtime/gc/space/space.h index 37d7c80..c9022f1 100644 --- a/runtime/gc/space/space.h +++ b/runtime/gc/space/space.h @@ -160,6 +160,9 @@ class Space { } virtual ContinuousMemMapAllocSpace* AsContinuousMemMapAllocSpace(); + // Returns true if objects in the space are movable. + virtual bool CanMoveObjects() const = 0; + virtual ~Space() {} protected: @@ -396,12 +399,9 @@ class ContinuousMemMapAllocSpace : public MemMapSpace, public AllocSpace { // Swap the live and mark bitmaps of this space. This is used by the GC for concurrent sweeping. void SwapBitmaps(); - // Free all memory associated with this space. + // Reset the space back to an empty space and release memory. virtual void Clear() = 0; - // Reset the space back to an empty space. - virtual void Reset() = 0; - accounting::SpaceBitmap* GetLiveBitmap() const { return live_bitmap_.get(); } diff --git a/runtime/gc/space/valgrind_malloc_space-inl.h b/runtime/gc/space/valgrind_malloc_space-inl.h index ed97e60..966c276 100644 --- a/runtime/gc/space/valgrind_malloc_space-inl.h +++ b/runtime/gc/space/valgrind_malloc_space-inl.h @@ -95,8 +95,10 @@ template <typename S, typename A> ValgrindMallocSpace<S, A>::ValgrindMallocSpace(const std::string& name, MemMap* mem_map, A allocator, byte* begin, byte* end, byte* limit, size_t growth_limit, - size_t initial_size) : - S(name, mem_map, allocator, begin, end, limit, growth_limit) { + size_t initial_size, + bool can_move_objects, size_t starting_size) : + S(name, mem_map, allocator, begin, end, limit, growth_limit, can_move_objects, starting_size, + initial_size) { VALGRIND_MAKE_MEM_UNDEFINED(mem_map->Begin() + initial_size, mem_map->Size() - initial_size); } diff --git a/runtime/gc/space/valgrind_malloc_space.h b/runtime/gc/space/valgrind_malloc_space.h index 6b755c4..200ad83 100644 --- a/runtime/gc/space/valgrind_malloc_space.h +++ b/runtime/gc/space/valgrind_malloc_space.h @@ -48,7 +48,7 @@ class ValgrindMallocSpace FINAL : public BaseMallocSpaceType { ValgrindMallocSpace(const std::string& name, MemMap* mem_map, AllocatorType allocator, byte* begin, byte* end, byte* limit, size_t growth_limit, - size_t initial_size); + size_t initial_size, bool can_move_objects, size_t starting_size); virtual ~ValgrindMallocSpace() {} private: diff --git a/runtime/gc/space/zygote_space.cc b/runtime/gc/space/zygote_space.cc index d1c3d03..a60ab38 100644 --- a/runtime/gc/space/zygote_space.cc +++ b/runtime/gc/space/zygote_space.cc @@ -61,10 +61,6 @@ void ZygoteSpace::Clear() { LOG(FATAL) << "Unimplemented"; } -void ZygoteSpace::Reset() { - LOG(FATAL) << "Unimplemented"; -} - ZygoteSpace::ZygoteSpace(const std::string& name, MemMap* mem_map, size_t objects_allocated) : ContinuousMemMapAllocSpace(name, mem_map, mem_map->Begin(), mem_map->End(), mem_map->End(), kGcRetentionPolicyFullCollect), diff --git a/runtime/gc/space/zygote_space.h b/runtime/gc/space/zygote_space.h index 8880548..30370aa 100644 --- a/runtime/gc/space/zygote_space.h +++ b/runtime/gc/space/zygote_space.h @@ -72,7 +72,10 @@ class ZygoteSpace FINAL : public ContinuousMemMapAllocSpace { } void Clear() OVERRIDE; - void Reset() OVERRIDE; + + bool CanMoveObjects() const OVERRIDE { + return false; + } protected: virtual accounting::SpaceBitmap::SweepCallback* GetSweepCallback() { |