OSDN Git Service

Enable large object space for zygote.
authorMathieu Chartier <mathieuc@google.com>
Wed, 20 Aug 2014 01:24:04 +0000 (18:24 -0700)
committerMathieu Chartier <mathieuc@google.com>
Wed, 20 Aug 2014 16:49:55 +0000 (09:49 -0700)
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

runtime/gc/accounting/mod_union_table.cc
runtime/gc/accounting/mod_union_table.h
runtime/gc/heap.cc
runtime/gc/heap.h

index 2686af0..3acf80d 100644 (file)
@@ -72,9 +72,11 @@ class ModUnionClearCardVisitor {
 
 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 @@ class ModUnionUpdateObjectReferencesVisitor {
       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 @@ class ModUnionUpdateObjectReferencesVisitor {
  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::ClearCards() {
 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 @@ void ModUnionTableCardCache::Dump(std::ostream& os) {
   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
index 449e171..f67dc27 100644 (file)
@@ -65,6 +65,9 @@ class ModUnionTable {
   // 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 @@ class ModUnionTableReferenceCache : public ModUnionTable {
 
   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 @@ class ModUnionTableCardCache : public ModUnionTable {
 
   void Dump(std::ostream& os);
 
+  void SetCards() OVERRIDE;
+
  protected:
   // Cleared card array, used to update the mod-union table.
   CardSet cleared_cards_;
index 821d22f..f0b7685 100644 (file)
@@ -128,8 +128,8 @@ Heap::Heap(size_t initial_size, size_t growth_limit, size_t min_free, size_t max
       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 @@ 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 (!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 @@ void Heap::CreateMainMallocSpace(MemMap* mem_map, size_t initial_size, size_t gr
     // 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 @@ void Heap::DumpGcPerformanceInfo(std::ostream& os) {
     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 @@ void Heap::PreZygoteFork() {
   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 @@ void Heap::PreZygoteFork() {
     // 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 @@ collector::GcType Heap::CollectGarbageInternal(collector::GcType gc_type, GcCaus
   // 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 @@ void Heap::GrowForUtilization(collector::GarbageCollector* collector_ran) {
     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 @@ void Heap::RegisterNativeAllocation(JNIEnv* env, int bytes) {
   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
index d5b49d8..ed93ad9 100644 (file)
@@ -79,6 +79,7 @@ namespace allocator {
 namespace space {
   class AllocSpace;
   class BumpPointerSpace;
+  class ContinuousMemMapAllocSpace;
   class DiscontinuousSpace;
   class DlMallocSpace;
   class ImageSpace;
@@ -87,7 +88,7 @@ namespace space {
   class RosAllocSpace;
   class Space;
   class SpaceTest;
-  class ContinuousMemMapAllocSpace;
+  class ZygoteSpace;
 }  // namespace space
 
 class AgeCardVisitor {
@@ -599,6 +600,10 @@ class Heap {
     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 @@ class Heap {
   // 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_;