From 654dd48e2230e16bfaa225decce72b52642e2f78 Mon Sep 17 00:00:00 2001 From: Hiroshi Yamauchi Date: Wed, 9 Jul 2014 12:54:32 -0700 Subject: [PATCH] Improve the OOME fragmentation message. Change-Id: I390d3622f8d572ec7e34ea6dff9e1e0936e81ac1 --- runtime/gc/allocator/rosalloc.cc | 28 ++++++++++++++++++++++++++++ runtime/gc/allocator/rosalloc.h | 2 ++ runtime/gc/heap.cc | 38 ++++++++++++-------------------------- runtime/gc/heap.h | 3 +-- runtime/gc/space/dlmalloc_space.cc | 24 ++++++++++++++++++++++++ runtime/gc/space/dlmalloc_space.h | 3 +++ runtime/gc/space/malloc_space.h | 3 +++ runtime/gc/space/rosalloc_space.h | 4 ++++ 8 files changed, 77 insertions(+), 28 deletions(-) diff --git a/runtime/gc/allocator/rosalloc.cc b/runtime/gc/allocator/rosalloc.cc index 722576f16..d26635faf 100644 --- a/runtime/gc/allocator/rosalloc.cc +++ b/runtime/gc/allocator/rosalloc.cc @@ -2175,6 +2175,34 @@ size_t RosAlloc::ReleasePageRange(byte* start, byte* end) { return reclaimed_bytes; } +void RosAlloc::LogFragmentationAllocFailure(std::ostream& os, size_t failed_alloc_bytes) { + Thread* self = Thread::Current(); + size_t largest_continuous_free_pages = 0; + WriterMutexLock wmu(self, bulk_free_lock_); + MutexLock mu(self, lock_); + for (FreePageRun* fpr : free_page_runs_) { + largest_continuous_free_pages = std::max(largest_continuous_free_pages, + fpr->ByteSize(this)); + } + if (failed_alloc_bytes > kLargeSizeThreshold) { + // Large allocation. + size_t required_bytes = RoundUp(failed_alloc_bytes, kPageSize); + if (required_bytes > largest_continuous_free_pages) { + os << "; failed due to fragmentation (required continguous free " + << required_bytes << " bytes where largest contiguous free " + << largest_continuous_free_pages << " bytes)"; + } + } else { + // Non-large allocation. + size_t required_bytes = numOfPages[SizeToIndex(failed_alloc_bytes)] * kPageSize; + if (required_bytes > largest_continuous_free_pages) { + os << "; failed due to fragmentation (required continguous free " + << required_bytes << " bytes for a new buffer where largest contiguous free " + << largest_continuous_free_pages << " bytes)"; + } + } +} + } // namespace allocator } // namespace gc } // namespace art diff --git a/runtime/gc/allocator/rosalloc.h b/runtime/gc/allocator/rosalloc.h index fad0dc888..85a822580 100644 --- a/runtime/gc/allocator/rosalloc.h +++ b/runtime/gc/allocator/rosalloc.h @@ -590,6 +590,8 @@ class RosAlloc { // Verify for debugging. void Verify() EXCLUSIVE_LOCKS_REQUIRED(Locks::mutator_lock_); + + void LogFragmentationAllocFailure(std::ostream& os, size_t failed_alloc_bytes); }; } // namespace allocator diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc index e9adca07c..19715e933 100644 --- a/runtime/gc/heap.cc +++ b/runtime/gc/heap.cc @@ -805,37 +805,23 @@ space::ImageSpace* Heap::GetImageSpace() const { return NULL; } -static void MSpaceChunkCallback(void* start, void* end, size_t used_bytes, void* arg) { - size_t chunk_size = reinterpret_cast(end) - reinterpret_cast(start); - if (used_bytes < chunk_size) { - size_t chunk_free_bytes = chunk_size - used_bytes; - size_t& max_contiguous_allocation = *reinterpret_cast(arg); - max_contiguous_allocation = std::max(max_contiguous_allocation, chunk_free_bytes); - } -} - -void Heap::ThrowOutOfMemoryError(Thread* self, size_t byte_count, bool large_object_allocation) { +void Heap::ThrowOutOfMemoryError(Thread* self, size_t byte_count, AllocatorType allocator_type) { std::ostringstream oss; size_t total_bytes_free = GetFreeMemory(); oss << "Failed to allocate a " << byte_count << " byte allocation with " << total_bytes_free << " free bytes"; // If the allocation failed due to fragmentation, print out the largest continuous allocation. - if (!large_object_allocation && total_bytes_free >= byte_count) { - size_t max_contiguous_allocation = 0; - for (const auto& space : continuous_spaces_) { - if (space->IsMallocSpace()) { - // To allow the Walk/InspectAll() to exclusively-lock the mutator - // lock, temporarily release the shared access to the mutator - // lock here by transitioning to the suspended state. - Locks::mutator_lock_->AssertSharedHeld(self); - self->TransitionFromRunnableToSuspended(kSuspended); - space->AsMallocSpace()->Walk(MSpaceChunkCallback, &max_contiguous_allocation); - self->TransitionFromSuspendedToRunnable(); - Locks::mutator_lock_->AssertSharedHeld(self); - } + if (allocator_type != kAllocatorTypeLOS && total_bytes_free >= byte_count) { + space::MallocSpace* space = nullptr; + if (allocator_type == kAllocatorTypeNonMoving) { + space = non_moving_space_; + } else if (allocator_type == kAllocatorTypeRosAlloc || + allocator_type == kAllocatorTypeDlMalloc) { + space = main_space_; + } + if (space != nullptr) { + space->LogFragmentationAllocFailure(oss, byte_count); } - oss << "; failed due to fragmentation (largest possible contiguous allocation " - << max_contiguous_allocation << " bytes)"; } self->ThrowOutOfMemoryError(oss.str().c_str()); } @@ -1188,7 +1174,7 @@ mirror::Object* Heap::AllocateInternalWithGc(Thread* self, AllocatorType allocat } ptr = TryToAllocate(self, allocator, alloc_size, bytes_allocated, usable_size); if (ptr == nullptr) { - ThrowOutOfMemoryError(self, alloc_size, allocator == kAllocatorTypeLOS); + ThrowOutOfMemoryError(self, alloc_size, allocator); } return ptr; } diff --git a/runtime/gc/heap.h b/runtime/gc/heap.h index c9ea03e45..07db169f3 100644 --- a/runtime/gc/heap.h +++ b/runtime/gc/heap.h @@ -194,7 +194,6 @@ class Heap { 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); void RegisterNativeAllocation(JNIEnv* env, int bytes); void RegisterNativeFree(JNIEnv* env, int bytes); @@ -628,7 +627,7 @@ class Heap { size_t* usable_size) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - void ThrowOutOfMemoryError(Thread* self, size_t byte_count, bool large_object_allocation) + void ThrowOutOfMemoryError(Thread* self, size_t byte_count, AllocatorType allocator_type) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); template diff --git a/runtime/gc/space/dlmalloc_space.cc b/runtime/gc/space/dlmalloc_space.cc index 5123e4787..456d1b31e 100644 --- a/runtime/gc/space/dlmalloc_space.cc +++ b/runtime/gc/space/dlmalloc_space.cc @@ -304,6 +304,30 @@ void DlMallocSpace::CheckMoreCoreForPrecondition() { } #endif +static void MSpaceChunkCallback(void* start, void* end, size_t used_bytes, void* arg) { + size_t chunk_size = reinterpret_cast(end) - reinterpret_cast(start); + if (used_bytes < chunk_size) { + size_t chunk_free_bytes = chunk_size - used_bytes; + size_t& max_contiguous_allocation = *reinterpret_cast(arg); + max_contiguous_allocation = std::max(max_contiguous_allocation, chunk_free_bytes); + } +} + +void DlMallocSpace::LogFragmentationAllocFailure(std::ostream& os, size_t failed_alloc_bytes) { + Thread* self = Thread::Current(); + size_t max_contiguous_allocation = 0; + // To allow the Walk/InspectAll() to exclusively-lock the mutator + // lock, temporarily release the shared access to the mutator + // lock here by transitioning to the suspended state. + Locks::mutator_lock_->AssertSharedHeld(self); + self->TransitionFromRunnableToSuspended(kSuspended); + Walk(MSpaceChunkCallback, &max_contiguous_allocation); + self->TransitionFromSuspendedToRunnable(); + Locks::mutator_lock_->AssertSharedHeld(self); + os << "; failed due to fragmentation (largest possible contiguous allocation " + << max_contiguous_allocation << " bytes)"; +} + } // namespace space } // namespace gc } // namespace art diff --git a/runtime/gc/space/dlmalloc_space.h b/runtime/gc/space/dlmalloc_space.h index accd26bd2..7aff14b66 100644 --- a/runtime/gc/space/dlmalloc_space.h +++ b/runtime/gc/space/dlmalloc_space.h @@ -124,6 +124,9 @@ class DlMallocSpace : public MallocSpace { return this; } + void LogFragmentationAllocFailure(std::ostream& os, size_t failed_alloc_bytes) OVERRIDE + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + protected: DlMallocSpace(const std::string& name, MemMap* mem_map, void* mspace, byte* begin, byte* end, byte* limit, size_t growth_limit, bool can_move_objects, size_t starting_size, diff --git a/runtime/gc/space/malloc_space.h b/runtime/gc/space/malloc_space.h index d24016cb1..6f49fbf20 100644 --- a/runtime/gc/space/malloc_space.h +++ b/runtime/gc/space/malloc_space.h @@ -19,6 +19,7 @@ #include "space.h" +#include #include #include @@ -132,6 +133,8 @@ class MallocSpace : public ContinuousMemMapAllocSpace { return can_move_objects_; } + virtual void LogFragmentationAllocFailure(std::ostream& os, size_t failed_alloc_bytes) = 0; + protected: MallocSpace(const std::string& name, MemMap* mem_map, byte* begin, byte* end, byte* limit, size_t growth_limit, bool create_bitmaps, bool can_move_objects, diff --git a/runtime/gc/space/rosalloc_space.h b/runtime/gc/space/rosalloc_space.h index 2934af87c..f50530576 100644 --- a/runtime/gc/space/rosalloc_space.h +++ b/runtime/gc/space/rosalloc_space.h @@ -120,6 +120,10 @@ class RosAllocSpace : public MallocSpace { virtual ~RosAllocSpace(); + void LogFragmentationAllocFailure(std::ostream& os, size_t failed_alloc_bytes) OVERRIDE { + rosalloc_->LogFragmentationAllocFailure(os, failed_alloc_bytes); + } + protected: RosAllocSpace(const std::string& name, MemMap* mem_map, allocator::RosAlloc* rosalloc, byte* begin, byte* end, byte* limit, size_t growth_limit, bool can_move_objects, -- 2.11.0