Enable large object space for zygote.
We now enable the large object space before the zygote fork.
This reduces the size of the zygote and removes the need for
excessive explicit GCs during phone booting.
Changed the card set mod union table to support forgetting cards.
If a card has no non null references which are in another space
then it is removed from the set.
Added logging of the zygote size when you do a SIGQUIT.
Dalvik PSS is the same or slightly lower (1-3%).
Zygote space size:
Before: 15MB
After: 8MB (+ some large objects).
TODO: Combine remembered sets and mod union tables into a single
interface.
Bug: 16398684
Change-Id: Ie48cdf35004a0a37eedb1ccc1bf214b1fa9e0cca
diff --git a/runtime/gc/accounting/mod_union_table.cc b/runtime/gc/accounting/mod_union_table.cc
index 2686af0..3acf80d 100644
--- a/runtime/gc/accounting/mod_union_table.cc
+++ b/runtime/gc/accounting/mod_union_table.cc
@@ -72,9 +72,11 @@
class ModUnionUpdateObjectReferencesVisitor {
public:
- ModUnionUpdateObjectReferencesVisitor(MarkHeapReferenceCallback* callback, void* arg)
- : callback_(callback),
- arg_(arg) {
+ ModUnionUpdateObjectReferencesVisitor(MarkHeapReferenceCallback* callback, void* arg,
+ space::ContinuousSpace* from_space,
+ bool* contains_reference_to_other_space)
+ : callback_(callback), arg_(arg), from_space_(from_space),
+ contains_reference_to_other_space_(contains_reference_to_other_space) {
}
// Extra parameters are required since we use this same visitor signature for checking objects.
@@ -82,7 +84,9 @@
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
// Only add the reference if it is non null and fits our criteria.
mirror::HeapReference<Object>* obj_ptr = obj->GetFieldObjectReferenceAddr(offset);
- if (obj_ptr->AsMirrorPtr() != nullptr) {
+ mirror::Object* ref = obj_ptr->AsMirrorPtr();
+ if (ref != nullptr && !from_space_->HasAddress(ref)) {
+ *contains_reference_to_other_space_ = true;
callback_(obj_ptr, arg_);
}
}
@@ -90,24 +94,36 @@
private:
MarkHeapReferenceCallback* const callback_;
void* arg_;
+ // Space which we are scanning
+ space::ContinuousSpace* const from_space_;
+ // Set if we have any references to another space.
+ bool* const contains_reference_to_other_space_;
};
class ModUnionScanImageRootVisitor {
public:
- ModUnionScanImageRootVisitor(MarkHeapReferenceCallback* callback, void* arg)
- : callback_(callback), arg_(arg) {}
+ ModUnionScanImageRootVisitor(MarkHeapReferenceCallback* callback, void* arg,
+ space::ContinuousSpace* from_space,
+ bool* contains_reference_to_other_space)
+ : callback_(callback), arg_(arg), from_space_(from_space),
+ contains_reference_to_other_space_(contains_reference_to_other_space) {}
void operator()(Object* root) const
EXCLUSIVE_LOCKS_REQUIRED(Locks::heap_bitmap_lock_)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
DCHECK(root != NULL);
- ModUnionUpdateObjectReferencesVisitor ref_visitor(callback_, arg_);
+ ModUnionUpdateObjectReferencesVisitor ref_visitor(callback_, arg_, from_space_,
+ contains_reference_to_other_space_);
root->VisitReferences<kMovingClasses>(ref_visitor, VoidFunctor());
}
private:
MarkHeapReferenceCallback* const callback_;
void* const arg_;
+ // Space which we are scanning
+ space::ContinuousSpace* const from_space_;
+ // Set if we have any references to another space.
+ bool* const contains_reference_to_other_space_;
};
void ModUnionTableReferenceCache::ClearCards() {
@@ -313,12 +329,20 @@
void ModUnionTableCardCache::UpdateAndMarkReferences(MarkHeapReferenceCallback* callback,
void* arg) {
CardTable* card_table = heap_->GetCardTable();
- ModUnionScanImageRootVisitor scan_visitor(callback, arg);
ContinuousSpaceBitmap* bitmap = space_->GetLiveBitmap();
- for (const byte* card_addr : cleared_cards_) {
- uintptr_t start = reinterpret_cast<uintptr_t>(card_table->AddrFromCard(card_addr));
+ bool reference_to_other_space = false;
+ ModUnionScanImageRootVisitor scan_visitor(callback, arg, space_, &reference_to_other_space);
+ for (auto it = cleared_cards_.begin(), end = cleared_cards_.end(); it != end; ) {
+ uintptr_t start = reinterpret_cast<uintptr_t>(card_table->AddrFromCard(*it));
DCHECK(space_->HasAddress(reinterpret_cast<Object*>(start)));
+ reference_to_other_space = false;
bitmap->VisitMarkedRange(start, start + CardTable::kCardSize, scan_visitor);
+ if (!reference_to_other_space) {
+ // No non null reference to another space, remove the card.
+ it = cleared_cards_.erase(it);
+ } else {
+ ++it;
+ }
}
}
@@ -333,6 +357,17 @@
os << "]";
}
+void ModUnionTableCardCache::SetCards() {
+ CardTable* card_table = heap_->GetCardTable();
+ for (byte* addr = space_->Begin(); addr < AlignUp(space_->End(), CardTable::kCardSize);
+ addr += CardTable::kCardSize) {
+ cleared_cards_.insert(card_table->CardFromAddr(addr));
+ }
+}
+
+void ModUnionTableReferenceCache::SetCards() {
+}
+
} // namespace accounting
} // namespace gc
} // namespace art
diff --git a/runtime/gc/accounting/mod_union_table.h b/runtime/gc/accounting/mod_union_table.h
index 449e171..f67dc27 100644
--- a/runtime/gc/accounting/mod_union_table.h
+++ b/runtime/gc/accounting/mod_union_table.h
@@ -65,6 +65,9 @@
// determining references to track.
virtual void ClearCards() = 0;
+ // Set all the cards.
+ virtual void SetCards() = 0;
+
// Update the mod-union table using data stored by ClearCards. There may be multiple ClearCards
// before a call to update, for example, back-to-back sticky GCs. Also mark references to other
// spaces which are stored in the mod-union table.
@@ -120,6 +123,8 @@
void Dump(std::ostream& os) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ void SetCards() OVERRIDE;
+
protected:
// Cleared card array, used to update the mod-union table.
ModUnionTable::CardSet cleared_cards_;
@@ -150,6 +155,8 @@
void Dump(std::ostream& os);
+ void SetCards() OVERRIDE;
+
protected:
// Cleared card array, used to update the mod-union table.
CardSet cleared_cards_;
diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc
index 821d22f..f0b7685 100644
--- a/runtime/gc/heap.cc
+++ b/runtime/gc/heap.cc
@@ -128,8 +128,8 @@
long_gc_log_threshold_(long_gc_log_threshold),
ignore_max_footprint_(ignore_max_footprint),
zygote_creation_lock_("zygote creation lock", kZygoteCreationLock),
- have_zygote_space_(false),
- large_object_threshold_(std::numeric_limits<size_t>::max()), // Starts out disabled.
+ zygote_space_(nullptr),
+ large_object_threshold_(kDefaultLargeObjectThreshold), // Starts out disabled.
collector_type_running_(kCollectorTypeNone),
last_gc_type_(collector::kGcTypeNone),
next_gc_type_(collector::kGcTypePartial),
@@ -190,7 +190,6 @@
// If we aren't the zygote, switch to the default non zygote allocator. This may update the
// entrypoints.
if (!Runtime::Current()->IsZygote()) {
- large_object_threshold_ = kDefaultLargeObjectThreshold;
// Background compaction is currently not supported for command line runs.
if (background_collector_type_ != foreground_collector_type_) {
VLOG(heap) << "Disabling background compaction for non zygote";
@@ -468,7 +467,7 @@
// 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.
// We never have homogeneous compaction with GSS and don't need a space with movable objects.
- can_move_objects = !have_zygote_space_ && foreground_collector_type_ != kCollectorTypeGSS;
+ can_move_objects = !HasZygoteSpace() && foreground_collector_type_ != kCollectorTypeGSS;
}
if (collector::SemiSpace::kUseRememberedSet && main_space_ != nullptr) {
RemoveRememberedSet(main_space_);
@@ -801,6 +800,9 @@
os << "Mean allocation time: " << PrettyDuration(allocation_time / total_objects_allocated)
<< "\n";
}
+ if (HasZygoteSpace()) {
+ os << "Zygote space size " << PrettySize(zygote_space_->Size()) << "\n";
+ }
os << "Total mutator paused time: " << PrettyDuration(total_paused_time) << "\n";
os << "Total time waiting for GC to complete: " << PrettyDuration(total_wait_time_) << "\n";
os << "Approximate GC data structures memory overhead: " << gc_memory_overhead_.LoadRelaxed();
@@ -1823,7 +1825,8 @@
Thread* self = Thread::Current();
MutexLock mu(self, zygote_creation_lock_);
// Try to see if we have any Zygote spaces.
- if (have_zygote_space_) {
+ if (HasZygoteSpace()) {
+ LOG(WARNING) << __FUNCTION__ << " called when we already have a zygote space.";
return;
}
VLOG(heap) << "Starting PreZygoteFork";
@@ -1897,26 +1900,26 @@
// from this point on.
RemoveRememberedSet(old_alloc_space);
}
- space::ZygoteSpace* zygote_space = old_alloc_space->CreateZygoteSpace("alloc space",
- low_memory_mode_,
- &non_moving_space_);
+ zygote_space_ = old_alloc_space->CreateZygoteSpace("alloc space", low_memory_mode_,
+ &non_moving_space_);
CHECK(!non_moving_space_->CanMoveObjects());
if (same_space) {
main_space_ = non_moving_space_;
SetSpaceAsDefault(main_space_);
}
delete old_alloc_space;
- CHECK(zygote_space != nullptr) << "Failed creating zygote space";
- AddSpace(zygote_space);
+ CHECK(HasZygoteSpace()) << "Failed creating zygote space";
+ AddSpace(zygote_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;
// Create the zygote space mod union table.
accounting::ModUnionTable* mod_union_table =
- new accounting::ModUnionTableCardCache("zygote space mod-union table", this, zygote_space);
+ new accounting::ModUnionTableCardCache("zygote space mod-union table", this,
+ zygote_space_);
CHECK(mod_union_table != nullptr) << "Failed to create zygote space mod-union table";
+ // Set all the cards in the mod-union table since we don't know which objects contain references
+ // to large objects.
+ mod_union_table->SetCards();
AddModUnionTable(mod_union_table);
if (collector::SemiSpace::kUseRememberedSet) {
// Add a new remembered set for the post-zygote non-moving space.
@@ -1986,7 +1989,7 @@
// If the heap can't run the GC, silently fail and return that no GC was run.
switch (gc_type) {
case collector::kGcTypePartial: {
- if (!have_zygote_space_) {
+ if (!HasZygoteSpace()) {
return collector::kGcTypeNone;
}
break;
@@ -2810,7 +2813,7 @@
next_gc_type_ = collector::kGcTypeSticky;
} else {
collector::GcType non_sticky_gc_type =
- have_zygote_space_ ? collector::kGcTypePartial : collector::kGcTypeFull;
+ HasZygoteSpace() ? collector::kGcTypePartial : collector::kGcTypeFull;
// Find what the next non sticky collector will be.
collector::GarbageCollector* non_sticky_collector = FindCollectorByGcType(non_sticky_gc_type);
// If the throughput of the current sticky GC >= throughput of the non sticky collector, then
@@ -3033,7 +3036,7 @@
size_t new_native_bytes_allocated = native_bytes_allocated_.FetchAndAddSequentiallyConsistent(bytes);
new_native_bytes_allocated += bytes;
if (new_native_bytes_allocated > native_footprint_gc_watermark_) {
- collector::GcType gc_type = have_zygote_space_ ? collector::kGcTypePartial :
+ collector::GcType gc_type = HasZygoteSpace() ? collector::kGcTypePartial :
collector::kGcTypeFull;
// The second watermark is higher than the gc watermark. If you hit this it means you are
diff --git a/runtime/gc/heap.h b/runtime/gc/heap.h
index d5b49d8..ed93ad9 100644
--- a/runtime/gc/heap.h
+++ b/runtime/gc/heap.h
@@ -79,6 +79,7 @@
namespace space {
class AllocSpace;
class BumpPointerSpace;
+ class ContinuousMemMapAllocSpace;
class DiscontinuousSpace;
class DlMallocSpace;
class ImageSpace;
@@ -87,7 +88,7 @@
class RosAllocSpace;
class Space;
class SpaceTest;
- class ContinuousMemMapAllocSpace;
+ class ZygoteSpace;
} // namespace space
class AgeCardVisitor {
@@ -599,6 +600,10 @@
return &reference_processor_;
}
+ bool HasZygoteSpace() const {
+ return zygote_space_ != nullptr;
+ }
+
private:
// Compact source space to target space.
void Compact(space::ContinuousMemMapAllocSpace* target_space,
@@ -849,8 +854,9 @@
// Lock which guards zygote space creation.
Mutex zygote_creation_lock_;
- // If we have a zygote space.
- bool have_zygote_space_;
+ // Non-null iff we have a zygote space. Doesn't contain the large objects allocated before
+ // zygote space creation.
+ space::ZygoteSpace* zygote_space_;
// Minimum allocation size of large object.
size_t large_object_threshold_;