result.sweeping_in_progress = false;
result.mark_compact_speed_in_bytes_per_ms = kMarkCompactSpeed;
result.incremental_marking_speed_in_bytes_per_ms = kMarkingSpeed;
+ result.scavenge_speed_in_bytes_per_ms = kScavengeSpeed;
+ result.available_new_space_memory = kNewSpaceCapacity;
+ result.new_space_capacity = kNewSpaceCapacity;
return result;
}
static const size_t kSizeOfObjects = 100 * MB;
static const size_t kMarkCompactSpeed = 200 * KB;
static const size_t kMarkingSpeed = 200 * KB;
+ static const size_t kScavengeSpeed = 100 * KB;
+ static const size_t kNewSpaceCapacity = 1 * MB;
private:
GCIdleTimeHandler handler_;
}
+TEST(GCIdleTimeHandler, EstimateScavengeTimeInitial) {
+ size_t size = 1 * MB;
+ size_t time = GCIdleTimeHandler::EstimateScavengeTime(size, 0);
+ EXPECT_EQ(size / GCIdleTimeHandler::kInitialConservativeScavengeSpeed, time);
+}
+
+
+TEST(GCIdleTimeHandler, EstimateScavengeTimeNonZero) {
+ size_t size = 1 * MB;
+ size_t speed = 1 * MB;
+ size_t time = GCIdleTimeHandler::EstimateScavengeTime(size, speed);
+ EXPECT_EQ(size / speed, time);
+}
+
+
TEST_F(GCIdleTimeHandlerTest, AfterContextDisposeLargeIdleTime) {
GCIdleTimeHandler::HeapState heap_state = DefaultHeapState();
heap_state.contexts_disposed = 1;
}
+size_t GCIdleTimeHandler::EstimateScavengeTime(
+ size_t new_space_size, size_t scavenge_speed_in_bytes_per_ms) {
+ if (scavenge_speed_in_bytes_per_ms == 0) {
+ scavenge_speed_in_bytes_per_ms = kInitialConservativeScavengeSpeed;
+ }
+ return new_space_size / scavenge_speed_in_bytes_per_ms;
+}
+
+
+// The following logic is implemented by the controller:
+// (1) If the new space is almost full and we can effort a Scavenge, then a
+// Scavenge is performed.
+// (2) If there is currently no MarkCompact idle round going on, we start a
+// new idle round if enough garbage was created or we received a context
+// disposal event. Otherwise we do not perform garbage collection to keep
+// system utilization low.
+// (3) If incremental marking is done, we perform a full garbage collection
+// if context was disposed or if we are allowed to still do full garbage
+// collections during this idle round or if we are not allowed to start
+// incremental marking. Otherwise we do not perform garbage collection to
+// keep system utilization low.
+// (4) If sweeping is in progress and we received a large enough idle time
+// request, we finalize sweeping here.
+// (5) If incremental marking is in progress, we perform a marking step. Note,
+// that this currently may trigger a full garbage collection.
GCIdleTimeAction GCIdleTimeHandler::Compute(size_t idle_time_in_ms,
HeapState heap_state) {
- if (IsIdleRoundFinished()) {
+ if (heap_state.available_new_space_memory < kNewSpaceAlmostFullTreshold &&
+ idle_time_in_ms >=
+ EstimateScavengeTime(heap_state.new_space_capacity,
+ heap_state.scavenge_speed_in_bytes_per_ms)) {
+ return GCIdleTimeAction::Scavenge();
+ }
+ if (IsMarkCompactIdleRoundFinished()) {
if (EnoughGarbageSinceLastIdleRound() || heap_state.contexts_disposed > 0) {
StartIdleRound();
} else {
// That is the maximum idle time we will have during frame rendering.
static const size_t kMaxFrameRenderingIdleTime = 16;
+ // If less than that much memory is left in the new space, we consider it
+ // as almost full and force a new space collection earlier in the idle time.
+ static const size_t kNewSpaceAlmostFullTreshold = 100 * KB;
+
+ // If we haven't recorded any scavenger events yet, we use a conservative
+ // lower bound for the scavenger speed.
+ static const size_t kInitialConservativeScavengeSpeed = 100 * KB;
+
struct HeapState {
int contexts_disposed;
size_t size_of_objects;
bool sweeping_in_progress;
size_t mark_compact_speed_in_bytes_per_ms;
size_t incremental_marking_speed_in_bytes_per_ms;
+ size_t scavenge_speed_in_bytes_per_ms;
+ size_t available_new_space_memory;
+ size_t new_space_capacity;
};
GCIdleTimeHandler()
static size_t EstimateMarkCompactTime(
size_t size_of_objects, size_t mark_compact_speed_in_bytes_per_ms);
+ static size_t EstimateScavengeTime(size_t new_space_size,
+ size_t scavenger_speed_in_bytes_per_ms);
+
private:
void StartIdleRound() { mark_compacts_since_idle_round_started_ = 0; }
- bool IsIdleRoundFinished() {
+ bool IsMarkCompactIdleRoundFinished() {
return mark_compacts_since_idle_round_started_ ==
kMaxMarkCompactsInIdleRound;
}
static_cast<size_t>(tracer()->MarkCompactSpeedInBytesPerMillisecond());
heap_state.incremental_marking_speed_in_bytes_per_ms = static_cast<size_t>(
tracer()->IncrementalMarkingSpeedInBytesPerMillisecond());
+ heap_state.scavenge_speed_in_bytes_per_ms =
+ static_cast<size_t>(tracer()->ScavengeSpeedInBytesPerMillisecond());
+ heap_state.available_new_space_memory = new_space_.Available();
+ heap_state.new_space_capacity = new_space_.Capacity();
GCIdleTimeAction action =
gc_idle_time_handler_.Compute(idle_time_in_ms, heap_state);
Address address() { return reinterpret_cast<Address>(this); }
- // Finds the NewSpacePage containg the given address.
+ // Finds the NewSpacePage containing the given address.
static inline NewSpacePage* FromAddress(Address address_in_page) {
Address page_start =
reinterpret_cast<Address>(reinterpret_cast<uintptr_t>(address_in_page) &