diff options
| -rw-r--r-- | compiler/image_writer.cc | 2 | ||||
| -rw-r--r-- | runtime/gc/heap.cc | 124 | ||||
| -rw-r--r-- | runtime/gc/heap.h | 9 | ||||
| -rw-r--r-- | runtime/globals.h | 44 |
4 files changed, 88 insertions, 91 deletions
diff --git a/compiler/image_writer.cc b/compiler/image_writer.cc index 740babd73a..90e2c65c89 100644 --- a/compiler/image_writer.cc +++ b/compiler/image_writer.cc @@ -525,7 +525,7 @@ void ImageWriter::CalculateNewObjectOffsets(size_t oat_loaded_size, size_t oat_d // Return to write header at start of image with future location of image_roots. At this point, // image_end_ is the size of the image (excluding bitmaps). - const size_t heap_bytes_per_bitmap_byte = 8 * gc::accounting::SpaceBitmap::kAlignment; + const size_t heap_bytes_per_bitmap_byte = kBitsPerByte * gc::accounting::SpaceBitmap::kAlignment; const size_t bitmap_bytes = RoundUp(image_end_, heap_bytes_per_bitmap_byte) / heap_bytes_per_bitmap_byte; ImageHeader image_header(reinterpret_cast<uint32_t>(image_begin_), diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc index 1cff719e65..33e6bfd7e0 100644 --- a/runtime/gc/heap.cc +++ b/runtime/gc/heap.cc @@ -62,6 +62,9 @@ #include "well_known_classes.h" namespace art { + +extern void SetQuickAllocEntryPointsAllocator(gc::AllocatorType allocator); + namespace gc { static constexpr bool kGCALotMode = false; @@ -69,6 +72,8 @@ static constexpr size_t kGcAlotInterval = KB; static constexpr bool kDumpGcPerformanceOnShutdown = false; // Minimum amount of remaining bytes before a concurrent GC is triggered. static constexpr size_t kMinConcurrentRemainingBytes = 128 * KB; +static constexpr AllocatorType kDefaultPreZygoteAllocator = kAllocatorTypeFreeList; +static constexpr AllocatorType kDefaultPostZygoteAllocator = kAllocatorTypeFreeList; 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, @@ -76,7 +81,7 @@ Heap::Heap(size_t initial_size, size_t growth_limit, size_t min_free, size_t max bool low_memory_mode, size_t long_pause_log_threshold, size_t long_gc_log_threshold, bool ignore_max_footprint) : non_moving_space_(nullptr), - concurrent_gc_(!kMovingCollector && concurrent_gc), + concurrent_gc_(concurrent_gc), parallel_gc_threads_(parallel_gc_threads), conc_gc_threads_(conc_gc_threads), low_memory_mode_(low_memory_mode), @@ -149,24 +154,25 @@ Heap::Heap(size_t initial_size, size_t growth_limit, size_t min_free, size_t max if (VLOG_IS_ON(heap) || VLOG_IS_ON(startup)) { LOG(INFO) << "Heap() entering"; } - + // If we aren't the zygote, switch to the default non zygote allocator. This may update the + // entrypoints. + if (!Runtime::Current()->IsZygote()) { + ChangeAllocator(kDefaultPreZygoteAllocator); + } live_bitmap_.reset(new accounting::HeapBitmap(this)); mark_bitmap_.reset(new accounting::HeapBitmap(this)); - // Requested begin for the alloc space, to follow the mapped image and oat files - byte* requested_alloc_space_begin = NULL; + byte* requested_alloc_space_begin = nullptr; if (!image_file_name.empty()) { space::ImageSpace* image_space = space::ImageSpace::Create(image_file_name.c_str()); - CHECK(image_space != NULL) << "Failed to create space for " << image_file_name; + CHECK(image_space != nullptr) << "Failed to create space for " << image_file_name; AddSpace(image_space); // Oat files referenced by image files immediately follow them in memory, ensure alloc space // 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 = - reinterpret_cast<byte*>(RoundUp(reinterpret_cast<uintptr_t>(oat_file_end_addr), - kPageSize)); + requested_alloc_space_begin = AlignUp(oat_file_end_addr, kPageSize); } } @@ -252,20 +258,19 @@ Heap::Heap(size_t initial_size, size_t growth_limit, size_t min_free, size_t max CHECK_NE(max_allowed_footprint_, 0U); // Create our garbage collectors. - if (!kMovingCollector) { - for (size_t i = 0; i < 2; ++i) { - const bool concurrent = i != 0; - garbage_collectors_.push_back(new collector::MarkSweep(this, concurrent)); - garbage_collectors_.push_back(new collector::PartialMarkSweep(this, concurrent)); - garbage_collectors_.push_back(new collector::StickyMarkSweep(this, concurrent)); - } - gc_plan_.push_back(collector::kGcTypeSticky); - gc_plan_.push_back(collector::kGcTypePartial); - gc_plan_.push_back(collector::kGcTypeFull); - } else { + for (size_t i = 0; i < 2; ++i) { + const bool concurrent = i != 0; + garbage_collectors_.push_back(new collector::MarkSweep(this, concurrent)); + garbage_collectors_.push_back(new collector::PartialMarkSweep(this, concurrent)); + garbage_collectors_.push_back(new collector::StickyMarkSweep(this, concurrent)); + } + gc_plan_.push_back(collector::kGcTypeSticky); + gc_plan_.push_back(collector::kGcTypePartial); + gc_plan_.push_back(collector::kGcTypeFull); + if (kMovingCollector) { + // TODO: Clean this up. semi_space_collector_ = new collector::SemiSpace(this); garbage_collectors_.push_back(semi_space_collector_); - gc_plan_.push_back(collector::kGcTypeFull); } if (running_on_valgrind_) { @@ -277,6 +282,15 @@ Heap::Heap(size_t initial_size, size_t growth_limit, size_t min_free, size_t max } } +void Heap::ChangeAllocator(AllocatorType allocator) { + DCHECK_NE(allocator, kAllocatorTypeLOS); + if (current_allocator_ != allocator) { + current_allocator_ = allocator; + SetQuickAllocEntryPointsAllocator(current_allocator_); + Runtime::Current()->GetInstrumentation()->ResetQuickAllocEntryPoints(); + } +} + bool Heap::IsCompilingBoot() const { for (const auto& space : continuous_spaces_) { if (space->IsImageSpace()) { @@ -1207,8 +1221,11 @@ void Heap::PreZygoteFork() { // Trim the pages at the end of the non moving space. non_moving_space_->Trim(); non_moving_space_->GetMemMap()->Protect(PROT_READ | PROT_WRITE); - // Create a new bump pointer space which we will compact into. + // Change the allocator to the post zygote one. + ChangeAllocator(kDefaultPostZygoteAllocator); + // TODO: Delete bump_pointer_space_ and temp_pointer_space_? if (semi_space_collector_ != nullptr) { + // Create a new bump pointer space which we will compact into. 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. @@ -1290,7 +1307,7 @@ void Heap::SwapSemiSpaces() { void Heap::Compact(space::ContinuousMemMapAllocSpace* target_space, space::ContinuousMemMapAllocSpace* source_space) { CHECK(kMovingCollector); - CHECK_NE(target_space, source_space) << "In-place compaction unsupported"; + CHECK_NE(target_space, source_space) << "In-place compaction currently unsupported"; if (target_space != source_space) { semi_space_collector_->SetFromSpace(source_space); semi_space_collector_->SetToSpace(target_space); @@ -1360,22 +1377,25 @@ collector::GcType Heap::CollectGarbageInternal(collector::GcType gc_type, GcCaus DCHECK_NE(gc_type, collector::kGcTypeNone); collector::GarbageCollector* collector = nullptr; - if (kMovingCollector) { + // TODO: Clean this up. + if (current_allocator_ == kAllocatorTypeBumpPointer) { gc_type = semi_space_collector_->GetGcType(); CHECK_EQ(temp_space_->GetObjectsAllocated(), 0U); semi_space_collector_->SetFromSpace(bump_pointer_space_); semi_space_collector_->SetToSpace(temp_space_); mprotect(temp_space_->Begin(), temp_space_->Capacity(), PROT_READ | PROT_WRITE); - } - for (const auto& cur_collector : garbage_collectors_) { - if (cur_collector->IsConcurrent() == concurrent_gc_ && - cur_collector->GetGcType() == gc_type) { - collector = cur_collector; - break; - } - } - if (kMovingCollector) { + collector = semi_space_collector_; gc_type = collector::kGcTypeFull; + } else if (current_allocator_ == kAllocatorTypeFreeList) { + for (const auto& cur_collector : garbage_collectors_) { + if (cur_collector->IsConcurrent() == concurrent_gc_ && + cur_collector->GetGcType() == gc_type) { + collector = cur_collector; + break; + } + } + } else { + LOG(FATAL) << "Invalid current allocator " << current_allocator_; } CHECK(collector != NULL) << "Could not find garbage collector with concurrent=" << concurrent_gc_ @@ -1930,7 +1950,6 @@ void Heap::GrowForUtilization(collector::GcType gc_type, uint64_t gc_duration) { const size_t bytes_allocated = GetBytesAllocated(); last_gc_size_ = bytes_allocated; last_gc_time_ns_ = NanoTime(); - size_t target_size; if (gc_type != collector::kGcTypeSticky) { // Grow the heap for non sticky GC. @@ -1950,7 +1969,6 @@ void Heap::GrowForUtilization(collector::GcType gc_type, uint64_t gc_duration) { } else { next_gc_type_ = collector::kGcTypePartial; } - // If we have freed enough memory, shrink the heap back down. if (bytes_allocated + max_free_ < max_allowed_footprint_) { target_size = bytes_allocated + max_free_; @@ -1958,11 +1976,9 @@ void Heap::GrowForUtilization(collector::GcType gc_type, uint64_t gc_duration) { target_size = std::max(bytes_allocated, max_allowed_footprint_); } } - if (!ignore_max_footprint_) { SetIdealFootprint(target_size); - - if (concurrent_gc_) { + if (concurrent_gc_ && AllocatorHasConcurrentGC(current_allocator_)) { // Calculate when to perform the next ConcurrentGC. // Calculate the estimated GC duration. double gc_duration_seconds = NsToMs(gc_duration) / 1000.0; @@ -1978,7 +1994,8 @@ void Heap::GrowForUtilization(collector::GcType gc_type, uint64_t gc_duration) { // Start a concurrent GC when we get close to the estimated remaining bytes. When the // allocation rate is very high, remaining_bytes could tell us that we should start a GC // right away. - concurrent_start_bytes_ = std::max(max_allowed_footprint_ - remaining_bytes, bytes_allocated); + concurrent_start_bytes_ = std::max(max_allowed_footprint_ - remaining_bytes, + bytes_allocated); } DCHECK_LE(concurrent_start_bytes_, max_allowed_footprint_); DCHECK_LE(max_allowed_footprint_, growth_limit_); @@ -1992,10 +2009,10 @@ void Heap::ClearGrowthLimit() { } void Heap::SetReferenceOffsets(MemberOffset reference_referent_offset, - MemberOffset reference_queue_offset, - MemberOffset reference_queueNext_offset, - MemberOffset reference_pendingNext_offset, - MemberOffset finalizer_reference_zombie_offset) { + MemberOffset reference_queue_offset, + MemberOffset reference_queueNext_offset, + MemberOffset reference_pendingNext_offset, + MemberOffset finalizer_reference_zombie_offset) { reference_referent_offset_ = reference_referent_offset; reference_queue_offset_ = reference_queue_offset; reference_queueNext_offset_ = reference_queueNext_offset; @@ -2029,27 +2046,6 @@ void Heap::AddFinalizerReference(Thread* self, mirror::Object* object) { arg_array.GetArray(), arg_array.GetNumBytes(), &result, 'V'); } -void Heap::PrintReferenceQueue(std::ostream& os, mirror::Object** queue) { - os << "Refernece queue " << queue << "\n"; - if (queue != nullptr) { - mirror::Object* list = *queue; - if (list != nullptr) { - mirror::Object* cur = list; - do { - mirror::Object* pending_next = - cur->GetFieldObject<mirror::Object*>(reference_pendingNext_offset_, false); - os << "PendingNext=" << pending_next; - if (cur->GetClass()->IsFinalizerReferenceClass()) { - os << " Zombie=" << - cur->GetFieldObject<mirror::Object*>(finalizer_reference_zombie_offset_, false); - } - os << "\n"; - cur = pending_next; - } while (cur != list); - } - } -} - void Heap::EnqueueClearedReferences() { if (!cleared_references_.IsEmpty()) { // When a runtime isn't started there are no reference queues to care about so ignore. @@ -2203,7 +2199,7 @@ void Heap::RegisterNativeAllocation(JNIEnv* env, int bytes) { // finalizers released native managed allocations. UpdateMaxNativeFootprint(); } else if (!IsGCRequestPending()) { - if (concurrent_gc_) { + if (concurrent_gc_ && AllocatorHasConcurrentGC(current_allocator_)) { RequestConcurrentGC(self); } else { CollectGarbageInternal(gc_type, kGcCauseForAlloc, false); diff --git a/runtime/gc/heap.h b/runtime/gc/heap.h index 6f94714be3..9215556257 100644 --- a/runtime/gc/heap.h +++ b/runtime/gc/heap.h @@ -189,6 +189,9 @@ class Heap { void RegisterNativeAllocation(JNIEnv* env, int bytes); void RegisterNativeFree(JNIEnv* env, int bytes); + // Change the allocator, updates entrypoints. + void ChangeAllocator(AllocatorType allocator); + // The given reference is believed to be to an object in the Java heap, check the soundness of it. void VerifyObjectImpl(const mirror::Object* o); void VerifyObject(const mirror::Object* o) { @@ -541,8 +544,6 @@ class Heap { bool IsEnqueued(mirror::Object* ref) const; void DelayReferenceReferent(mirror::Class* klass, mirror::Object* obj, RootVisitor mark_visitor, void* arg) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - // Print a reference queue. - void PrintReferenceQueue(std::ostream& os, mirror::Object** queue); // Run the finalizers. void RunFinalization(JNIEnv* env); @@ -628,7 +629,7 @@ class Heap { // What kind of concurrency behavior is the runtime after? True for concurrent mark sweep GC, // false for stop-the-world mark sweep. - const bool concurrent_gc_; + bool concurrent_gc_; // How many GC threads we may use for paused parts of garbage collection. const size_t parallel_gc_threads_; @@ -776,7 +777,7 @@ class Heap { UniquePtr<accounting::ObjectStack> live_stack_; // Allocator type. - const AllocatorType current_allocator_; + AllocatorType current_allocator_; const AllocatorType current_non_moving_allocator_; // Which GCs we run in order when we an allocation fails. diff --git a/runtime/globals.h b/runtime/globals.h index 1a25dfa4dd..c2fe67e083 100644 --- a/runtime/globals.h +++ b/runtime/globals.h @@ -26,61 +26,61 @@ typedef uint8_t byte; typedef intptr_t word; typedef uintptr_t uword; -const size_t KB = 1024; -const size_t MB = KB * KB; -const size_t GB = KB * KB * KB; +static constexpr size_t KB = 1024; +static constexpr size_t MB = KB * KB; +static constexpr size_t GB = KB * KB * KB; -const size_t kWordSize = sizeof(word); -const size_t kPointerSize = sizeof(void*); +static constexpr size_t kWordSize = sizeof(word); +static constexpr size_t kPointerSize = sizeof(void*); -const size_t kBitsPerByte = 8; -const size_t kBitsPerByteLog2 = 3; -const int kBitsPerWord = kWordSize * kBitsPerByte; -const size_t kWordHighBitMask = 1 << (kBitsPerWord - 1); +static constexpr size_t kBitsPerByte = 8; +static constexpr size_t kBitsPerByteLog2 = 3; +static constexpr int kBitsPerWord = kWordSize * kBitsPerByte; +static constexpr size_t kWordHighBitMask = 1 << (kBitsPerWord - 1); // Required stack alignment -const size_t kStackAlignment = 16; +static constexpr size_t kStackAlignment = 16; // Required object alignment -const size_t kObjectAlignment = 8; +static constexpr size_t kObjectAlignment = 8; // ARM instruction alignment. ARM processors require code to be 4-byte aligned, // but ARM ELF requires 8.. -const size_t kArmAlignment = 8; +static constexpr size_t kArmAlignment = 8; // MIPS instruction alignment. MIPS processors require code to be 4-byte aligned. // TODO: Can this be 4? -const size_t kMipsAlignment = 8; +static constexpr size_t kMipsAlignment = 8; // X86 instruction alignment. This is the recommended alignment for maximum performance. -const size_t kX86Alignment = 16; +static constexpr size_t kX86Alignment = 16; // System page size. We check this against sysconf(_SC_PAGE_SIZE) at runtime, but use a simple // compile-time constant so the compiler can generate better code. -const int kPageSize = 4096; +static constexpr int kPageSize = 4096; // Whether or not this is a debug build. Useful in conditionals where NDEBUG isn't. #if defined(NDEBUG) -const bool kIsDebugBuild = false; +static constexpr bool kIsDebugBuild = false; #else -const bool kIsDebugBuild = true; +static constexpr bool kIsDebugBuild = true; #endif // Whether or not this is a target (vs host) build. Useful in conditionals where ART_TARGET isn't. #if defined(ART_TARGET) -const bool kIsTargetBuild = true; +static constexpr bool kIsTargetBuild = true; #else -const bool kIsTargetBuild = false; +static constexpr bool kIsTargetBuild = false; #endif #if defined(ART_USE_PORTABLE_COMPILER) -constexpr bool kUsePortableCompiler = true; +static constexpr bool kUsePortableCompiler = true; #else -constexpr bool kUsePortableCompiler = false; +static constexpr bool kUsePortableCompiler = false; #endif // Garbage collector constants. -static constexpr bool kMovingCollector = false && !kUsePortableCompiler; +static constexpr bool kMovingCollector = true && !kUsePortableCompiler; // True if we allow moving classes. static constexpr bool kMovingClasses = false; // True if we allow moving fields. |