Compact zygote.
We now start out using the bump pointer allocator and switch to
the free list collector (ROSAlloc) after the zygote forks.
Before compaction:
Zygote size: 9060352
After compaction
Zygote size: 8425864
The main reason the size doesn't reduce more is that most of the
zygote space is non-movable objects allocated by
VMRuntime.newNonMovableObject. The objects which are non-movable
but could be movable include around 10000 classes and some number
of fields and methods.
Bug: 8981901
Change-Id: Iea21b70fb7af27cb7e92d72070d278a5cd4026ac
diff --git a/compiler/image_writer.cc b/compiler/image_writer.cc
index 740babd..90e2c65 100644
--- a/compiler/image_writer.cc
+++ b/compiler/image_writer.cc
@@ -525,7 +525,7 @@
// 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 1cff719..33e6bfd 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 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 @@
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 @@
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 @@
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 @@
}
}
+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 @@
// 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::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 @@
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 @@
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 @@
} 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 @@
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 @@
// 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::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 @@
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 @@
// 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 6f94714..9215556 100644
--- a/runtime/gc/heap.h
+++ b/runtime/gc/heap.h
@@ -189,6 +189,9 @@
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 @@
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 @@
// 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 @@
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 1a25dfa..c2fe67e 100644
--- a/runtime/globals.h
+++ b/runtime/globals.h
@@ -26,61 +26,61 @@
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.