[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_;