[vm] Switch Zones from arithmetic to geometric growth when they become large.

Avoids failing on Linux from exhausting Page Table Entries before exhausting physical memory or address space.

Change-Id: Idffbd0a2eb8b030f2afabb4c31135fb75deca59f
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/144669
Commit-Queue: Ryan Macnak <rmacnak@google.com>
Reviewed-by: Vyacheslav Egorov <vegorov@google.com>
diff --git a/runtime/vm/zone.cc b/runtime/vm/zone.cc
index b6d3727..7859112 100644
--- a/runtime/vm/zone.cc
+++ b/runtime/vm/zone.cc
@@ -40,7 +40,7 @@
   void* alignment_;
 
   // Computes the address of the nth byte in this segment.
-  uword address(int n) { return reinterpret_cast<uword>(this) + n; }
+  uword address(intptr_t n) { return reinterpret_cast<uword>(this) + n; }
 
   DISALLOW_IMPLICIT_CONSTRUCTORS(Segment);
 };
@@ -195,6 +195,7 @@
 #endif
   position_ = initial_buffer_.start();
   limit_ = initial_buffer_.end();
+  small_segment_capacity_ = 0;
   head_ = NULL;
   large_segments_ = NULL;
   previous_ = NULL;
@@ -252,8 +253,22 @@
     return AllocateLargeSegment(size);
   }
 
+  const intptr_t kSuperPageSize = 2 * MB;
+  intptr_t next_size;
+  if (small_segment_capacity_ < kSuperPageSize) {
+    // When the Zone is small, grow linearly to reduce size and use the segment
+    // cache to avoid expensive mmap calls.
+    next_size = kSegmentSize;
+  } else {
+    // When the Zone is large, grow geometrically to avoid Page Table Entry
+    // exhaustion. Using 1.125 ratio.
+    next_size = Utils::RoundUp(small_segment_capacity_ >> 3, kSuperPageSize);
+  }
+  ASSERT(next_size >= kSegmentSize);
+
   // Allocate another segment and chain it up.
-  head_ = Segment::New(kSegmentSize, head_);
+  head_ = Segment::New(next_size, head_);
+  small_segment_capacity_ += next_size;
 
   // Recompute 'position' and 'limit' based on the new head segment.
   uword result = Utils::RoundUp(head_->start(), kAlignment);
diff --git a/runtime/vm/zone.h b/runtime/vm/zone.h
index 7115cc71..548708d 100644
--- a/runtime/vm/zone.h
+++ b/runtime/vm/zone.h
@@ -155,6 +155,9 @@
   // implementation is in zone.cc.
   class Segment;
 
+  // Total size of all segments in [head_].
+  intptr_t small_segment_capacity_ = 0;
+
   // The current head segment; may be NULL.
   Segment* head_;