8380656: G1: Refactor G1IHOPControl

Co-authored-by: Thomas Schatzl <tschatzl@openjdk.org>
Reviewed-by: ayang, tschatzl
This commit is contained in:
Ivan Walulya 2026-03-27 11:20:05 +00:00
parent cca8c23871
commit b242eef312
5 changed files with 121 additions and 97 deletions

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2001, 2020, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2001, 2026, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -30,7 +30,7 @@
// Used to track time from the end of concurrent start to the first mixed GC.
// After calling the concurrent start/mixed gc notifications, the result can be
// obtained in last_marking_time() once, after which the tracking resets.
// obtained in get_and_reset_last_marking_time() once, after which the tracking resets.
// Any pauses recorded by add_pause() will be subtracted from that results.
class G1ConcurrentStartToMixedTimeTracker {
private:
@ -60,7 +60,7 @@ public:
}
}
double last_marking_time() {
double get_and_reset_last_marking_time() {
assert(has_result(), "Do not have all measurements yet.");
double result = (_mixed_start_time - _concurrent_start_end_time) - _total_pause_time;
reset();
@ -80,6 +80,8 @@ public:
}
}
bool is_active() const { return _active; }
// Returns whether we have a result that can be retrieved.
bool has_result() const { return _mixed_start_time > 0.0 && _concurrent_start_end_time > 0.0; }
};

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2015, 2025, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2015, 2026, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -38,18 +38,18 @@ double G1IHOPControl::predict(const TruncatedSeq* seq) const {
bool G1IHOPControl::have_enough_data_for_prediction() const {
assert(_is_adaptive, "precondition");
return ((size_t)_marking_times_s.num() >= G1AdaptiveIHOPNumInitialSamples) &&
((size_t)_allocation_rate_s.num() >= G1AdaptiveIHOPNumInitialSamples);
return ((size_t)_marking_start_to_mixed_time_s.num() >= G1AdaptiveIHOPNumInitialSamples) &&
((size_t)_old_gen_alloc_rate.num() >= G1AdaptiveIHOPNumInitialSamples);
}
double G1IHOPControl::last_marking_length_s() const {
return _marking_times_s.last();
double G1IHOPControl::last_marking_start_to_mixed_time_s() const {
return _marking_start_to_mixed_time_s.last();
}
size_t G1IHOPControl::actual_target_threshold() const {
size_t G1IHOPControl::effective_target_occupancy() const {
assert(_is_adaptive, "precondition");
// The actual target threshold takes the heap reserve and the expected waste in
// The effective target occupancy takes the heap reserve and the expected waste in
// free space into account.
// _heap_reserve is that part of the total heap capacity that is reserved for
// eventual promotion failure.
@ -79,9 +79,9 @@ G1IHOPControl::G1IHOPControl(double ihop_percent,
_last_allocation_time_s(0.0),
_old_gen_alloc_tracker(old_gen_alloc_tracker),
_predictor(predictor),
_marking_times_s(10, 0.05),
_allocation_rate_s(10, 0.05),
_last_unrestrained_young_size(0) {
_marking_start_to_mixed_time_s(10, 0.05),
_old_gen_alloc_rate(10, 0.05),
_expected_young_gen_at_first_mixed_gc(0) {
assert(_initial_ihop_percent >= 0.0 && _initial_ihop_percent <= 100.0,
"IHOP percent out of range: %.3f", ihop_percent);
assert(!_is_adaptive || _predictor != nullptr, "precondition");
@ -98,85 +98,104 @@ void G1IHOPControl::report_statistics(G1NewTracer* new_tracer, size_t non_young_
send_trace_event(new_tracer, non_young_occupancy);
}
void G1IHOPControl::update_allocation_info(double allocation_time_s, size_t additional_buffer_size) {
void G1IHOPControl::update_allocation_info(double allocation_time_s, size_t expected_young_gen_size) {
assert(allocation_time_s > 0, "Invalid allocation time: %.3f", allocation_time_s);
_last_allocation_time_s = allocation_time_s;
double alloc_rate = _old_gen_alloc_tracker->last_period_old_gen_growth() / allocation_time_s;
_allocation_rate_s.add(alloc_rate);
_last_unrestrained_young_size = additional_buffer_size;
_old_gen_alloc_rate.add(alloc_rate);
_expected_young_gen_at_first_mixed_gc = expected_young_gen_size;
}
void G1IHOPControl::update_marking_length(double marking_length_s) {
assert(marking_length_s >= 0.0, "Invalid marking length: %.3f", marking_length_s);
_marking_times_s.add(marking_length_s);
void G1IHOPControl::add_marking_start_to_mixed_length(double length_s) {
assert(length_s >= 0.0, "Invalid marking length: %.3f", length_s);
_marking_start_to_mixed_time_s.add(length_s);
}
size_t G1IHOPControl::get_conc_mark_start_threshold() {
// Determine the old generation occupancy threshold at which to start
// concurrent marking such that reclamation (first Mixed GC) begins
// before the heap reaches a critical occupancy level.
size_t G1IHOPControl::old_gen_threshold_for_conc_mark_start() {
guarantee(_target_occupancy > 0, "Target occupancy must be initialized");
if (!_is_adaptive || !have_enough_data_for_prediction()) {
return (size_t)(_initial_ihop_percent * _target_occupancy / 100.0);
}
double pred_marking_time = predict(&_marking_times_s);
double pred_rate = predict(&_allocation_rate_s);
size_t pred_bytes = (size_t)(pred_marking_time * pred_rate);
size_t predicted_needed = pred_bytes + _last_unrestrained_young_size;
size_t internal_threshold = actual_target_threshold();
// During the time between marking start and the first Mixed GC,
// additional memory will be consumed:
// - Old gen grows due to allocations:
// old_gen_alloc_bytes = old_gen_alloc_rate * marking_start_to_mixed_time
// - Young gen will occupy a certain size at the first Mixed GC:
// expected_young_gen_at_first_mixed_gc
double marking_start_to_mixed_time = predict(&_marking_start_to_mixed_time_s);
double old_gen_alloc_rate = predict(&_old_gen_alloc_rate);
size_t old_gen_alloc_bytes = (size_t)(marking_start_to_mixed_time * old_gen_alloc_rate);
return predicted_needed < internal_threshold
? internal_threshold - predicted_needed
// Therefore, the total heap occupancy at the first Mixed GC is:
// current_old_gen + old_gen_growth + expected_young_gen_at_first_mixed_gc
//
// To ensure this does not exceed the target_heap_occupancy, we work
// backwards to compute the old gen occupancy at which marking must start:
// mark_start_threshold = target_heap_occupancy -
// (old_gen_growth + expected_young_gen_at_first_mixed_gc)
size_t predicted_needed = old_gen_alloc_bytes + _expected_young_gen_at_first_mixed_gc;
size_t target_heap_occupancy = effective_target_occupancy();
return predicted_needed < target_heap_occupancy
? target_heap_occupancy - predicted_needed
: 0;
}
void G1IHOPControl::print_log(size_t non_young_occupancy) {
assert(_target_occupancy > 0, "Target occupancy still not updated yet.");
size_t cur_conc_mark_start_threshold = get_conc_mark_start_threshold();
log_debug(gc, ihop)("Basic information (value update), threshold: %zuB (%1.2f), target occupancy: %zuB, non-young occupancy: %zuB, "
"recent allocation size: %zuB, recent allocation duration: %1.2fms, recent old gen allocation rate: %1.2fB/s, recent marking phase length: %1.2fms",
cur_conc_mark_start_threshold,
percent_of(cur_conc_mark_start_threshold, _target_occupancy),
size_t old_gen_mark_start_threshold = old_gen_threshold_for_conc_mark_start();
log_debug(gc, ihop)("Basic information (value update), old-gen threshold: %zuB (%1.2f%%), target occupancy: %zuB, old-gen occupancy: %zuB (%1.2f%%), "
"recent old-gen allocation size: %zuB, recent allocation duration: %1.2fms, recent old-gen allocation rate: %1.2fB/s, recent marking phase length: %1.2fms",
old_gen_mark_start_threshold,
percent_of(old_gen_mark_start_threshold, _target_occupancy),
_target_occupancy,
non_young_occupancy,
percent_of(non_young_occupancy, _target_occupancy),
_old_gen_alloc_tracker->last_period_old_gen_bytes(),
_last_allocation_time_s * 1000.0,
_last_allocation_time_s > 0.0 ? _old_gen_alloc_tracker->last_period_old_gen_bytes() / _last_allocation_time_s : 0.0,
last_marking_length_s() * 1000.0);
last_marking_start_to_mixed_time_s() * 1000.0);
if (!_is_adaptive) {
return;
}
size_t actual_threshold = actual_target_threshold();
log_debug(gc, ihop)("Adaptive IHOP information (value update), threshold: %zuB (%1.2f), internal target threshold: %zuB, "
"non-young occupancy: %zuB, additional buffer size: %zuB, predicted old gen allocation rate: %1.2fB/s, "
"predicted marking phase length: %1.2fms, prediction active: %s",
cur_conc_mark_start_threshold,
percent_of(cur_conc_mark_start_threshold, actual_threshold),
actual_threshold,
size_t effective_target = effective_target_occupancy();
log_debug(gc, ihop)("Adaptive IHOP information (value update), prediction active: %s, old-gen threshold: %zuB (%1.2f%%), internal target occupancy: %zuB, "
"old-gen occupancy: %zuB, additional buffer size: %zuB, predicted old-gen allocation rate: %1.2fB/s, "
"predicted marking phase length: %1.2fms",
BOOL_TO_STR(have_enough_data_for_prediction()),
old_gen_mark_start_threshold,
percent_of(old_gen_mark_start_threshold, effective_target),
effective_target,
non_young_occupancy,
_last_unrestrained_young_size,
predict(&_allocation_rate_s),
predict(&_marking_times_s) * 1000.0,
have_enough_data_for_prediction() ? "true" : "false");
_expected_young_gen_at_first_mixed_gc,
predict(&_old_gen_alloc_rate),
predict(&_marking_start_to_mixed_time_s) * 1000.0);
}
void G1IHOPControl::send_trace_event(G1NewTracer* tracer, size_t non_young_occupancy) {
assert(_target_occupancy > 0, "Target occupancy still not updated yet.");
tracer->report_basic_ihop_statistics(get_conc_mark_start_threshold(),
tracer->report_basic_ihop_statistics(old_gen_threshold_for_conc_mark_start(),
_target_occupancy,
non_young_occupancy,
_old_gen_alloc_tracker->last_period_old_gen_bytes(),
_last_allocation_time_s,
last_marking_length_s());
last_marking_start_to_mixed_time_s());
if (_is_adaptive) {
tracer->report_adaptive_ihop_statistics(get_conc_mark_start_threshold(),
actual_target_threshold(),
tracer->report_adaptive_ihop_statistics(old_gen_threshold_for_conc_mark_start(),
effective_target_occupancy(),
non_young_occupancy,
_last_unrestrained_young_size,
predict(&_allocation_rate_s),
predict(&_marking_times_s),
_expected_young_gen_at_first_mixed_gc,
predict(&_old_gen_alloc_rate),
predict(&_marking_start_to_mixed_time_s),
have_enough_data_for_prediction());
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2015, 2025, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2015, 2026, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -58,8 +58,11 @@ class G1IHOPControl : public CHeapObj<mtGC> {
const G1OldGenAllocationTracker* _old_gen_alloc_tracker;
const G1Predictions* _predictor;
TruncatedSeq _marking_times_s;
TruncatedSeq _allocation_rate_s;
// Wall-clock time in seconds from marking start to the first mixed GC,
// excluding GC Pause time.
TruncatedSeq _marking_start_to_mixed_time_s;
// Old generation allocation rate in bytes per second.
TruncatedSeq _old_gen_alloc_rate;
// The most recent unrestrained size of the young gen. This is used as an additional
// factor in the calculation of the threshold, as the threshold is based on
@ -68,18 +71,18 @@ class G1IHOPControl : public CHeapObj<mtGC> {
// Since we cannot know what young gen sizes are used in the future, we will just
// use the current one. We expect that this one will be one with a fairly large size,
// as there is no marking or mixed gc that could impact its size too much.
size_t _last_unrestrained_young_size;
size_t _expected_young_gen_at_first_mixed_gc;
// Get a new prediction bounded below by zero from the given sequence.
double predict(const TruncatedSeq* seq) const;
bool have_enough_data_for_prediction() const;
double last_marking_length_s() const;
double last_marking_start_to_mixed_time_s() const;
// The "actual" target threshold the algorithm wants to keep during and at the
// end of marking. This is typically lower than the requested threshold, as the
// The "effective" target occupancy the algorithm wants to keep until the start
// of Mixed GCs. This is typically lower than the target occupancy, as the
// algorithm needs to consider restrictions by the environment.
size_t actual_target_threshold() const;
size_t effective_target_occupancy() const;
void print_log(size_t non_young_occupancy);
void send_trace_event(G1NewTracer* tracer, size_t non_young_occupancy);
@ -95,22 +98,24 @@ class G1IHOPControl : public CHeapObj<mtGC> {
// Adjust target occupancy.
void update_target_occupancy(size_t new_target_occupancy);
// Update information about time during which allocations in the Java heap occurred,
// how large these allocations were in bytes, and an additional buffer.
// The allocations should contain any amount of space made unusable for further
// allocation, e.g. any waste caused by TLAB allocation, space at the end of
// humongous objects that can not be used for allocation, etc.
// Together with the target occupancy, this additional buffer should contain the
// difference between old gen size and total heap size at the start of reclamation,
// and space required for that reclamation.
void update_allocation_info(double allocation_time_s, size_t additional_buffer_size);
void update_target_after_marking_phase();
// Update allocation rate information and current expected young gen size for the
// first mixed gc needed for the predictor. Allocation rate is given as the
// separately passed in allocation increment and the time passed (mutator time)
// for the latest allocation increment here. Allocation size is the memory needed
// during the mutator before and the first mixed gc pause itself.
// Contents include young gen at that point, and the memory required for evacuating
// the collection set in that first mixed gc (including waste caused by PLAB
// allocation etc.).
void update_allocation_info(double allocation_time_s, size_t expected_young_gen_size);
// Update the time spent in the mutator beginning from the end of concurrent start to
// the first mixed gc.
void update_marking_length(double marking_length_s);
void add_marking_start_to_mixed_length(double length_s);
// Get the current non-young occupancy at which concurrent marking should start.
size_t get_conc_mark_start_threshold();
size_t old_gen_threshold_for_conc_mark_start();
void report_statistics(G1NewTracer* tracer, size_t non_young_occupancy);
};

View File

@ -738,15 +738,15 @@ bool G1Policy::need_to_start_conc_mark(const char* source, size_t allocation_wor
return false;
}
size_t marking_initiating_used_threshold = _ihop_control->get_conc_mark_start_threshold();
size_t marking_initiating_old_gen_threshold = _ihop_control->old_gen_threshold_for_conc_mark_start();
size_t non_young_occupancy = _g1h->non_young_occupancy_after_allocation(allocation_word_size);
bool result = false;
if (non_young_occupancy > marking_initiating_used_threshold) {
if (non_young_occupancy > marking_initiating_old_gen_threshold) {
result = collector_state()->is_in_young_only_phase();
log_debug(gc, ergo, ihop)("%s non-young occupancy: %zuB allocation request: %zuB threshold: %zuB (%1.2f) source: %s",
result ? "Request concurrent cycle initiation (occupancy higher than threshold)" : "Do not request concurrent cycle initiation (still doing mixed collections)",
non_young_occupancy, allocation_word_size * HeapWordSize, marking_initiating_used_threshold, (double) marking_initiating_used_threshold / _g1h->capacity() * 100, source);
non_young_occupancy, allocation_word_size * HeapWordSize, marking_initiating_old_gen_threshold, (double) marking_initiating_old_gen_threshold / _g1h->capacity() * 100, source);
}
return result;
}
@ -972,8 +972,7 @@ void G1Policy::record_young_collection_end(bool concurrent_operation_is_full_mar
update_young_length_bounds();
_old_gen_alloc_tracker.reset_after_gc(_g1h->humongous_regions_count() * G1HeapRegion::GrainBytes);
if (update_ihop_prediction(app_time_ms / 1000.0,
G1CollectorState::is_young_only_pause(this_pause))) {
if (update_ihop_prediction(app_time_ms / 1000.0, is_young_only_pause)) {
_ihop_control->report_statistics(_g1h->gc_tracer_stw(), _g1h->non_young_occupancy_after_allocation(allocation_word_size));
}
} else {
@ -1030,14 +1029,13 @@ bool G1Policy::update_ihop_prediction(double mutator_time_s,
bool report = false;
double marking_to_mixed_time = -1.0;
if (!this_gc_was_young_only && _concurrent_start_to_mixed.has_result()) {
marking_to_mixed_time = _concurrent_start_to_mixed.last_marking_time();
double marking_to_mixed_time = _concurrent_start_to_mixed.get_and_reset_last_marking_time();
assert(marking_to_mixed_time > 0.0,
"Concurrent start to mixed time must be larger than zero but is %.3f",
marking_to_mixed_time);
if (marking_to_mixed_time > min_valid_time) {
_ihop_control->update_marking_length(marking_to_mixed_time);
_ihop_control->add_marking_start_to_mixed_length(marking_to_mixed_time);
report = true;
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2016, 2025, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2016, 2026, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -40,7 +40,7 @@ static void test_update(G1IHOPControl* ctrl,
test_update_allocation_tracker(alloc_tracker, alloc_amount);
for (int i = 0; i < 100; i++) {
ctrl->update_allocation_info(alloc_time, young_size);
ctrl->update_marking_length(mark_time);
ctrl->add_marking_start_to_mixed_length(mark_time);
}
}
@ -57,7 +57,7 @@ static void test_update_humongous(G1IHOPControl* ctrl,
alloc_tracker->reset_after_gc(humongous_bytes_after_last_gc);
for (int i = 0; i < 100; i++) {
ctrl->update_allocation_info(alloc_time, young_size);
ctrl->update_marking_length(mark_time);
ctrl->add_marking_start_to_mixed_length(mark_time);
}
}
@ -74,26 +74,26 @@ TEST_VM(G1IHOPControl, static_simple) {
G1IHOPControl ctrl(initial_ihop, &alloc_tracker, is_adaptive, nullptr, 0, 0);
ctrl.update_target_occupancy(100);
size_t threshold = ctrl.get_conc_mark_start_threshold();
size_t threshold = ctrl.old_gen_threshold_for_conc_mark_start();
EXPECT_EQ(initial_ihop, threshold);
test_update_allocation_tracker(&alloc_tracker, 100);
ctrl.update_allocation_info(100.0, 100);
threshold = ctrl.get_conc_mark_start_threshold();
threshold = ctrl.old_gen_threshold_for_conc_mark_start();
EXPECT_EQ(initial_ihop, threshold);
ctrl.update_marking_length(1000.0);
threshold = ctrl.get_conc_mark_start_threshold();
ctrl.add_marking_start_to_mixed_length(1000.0);
threshold = ctrl.old_gen_threshold_for_conc_mark_start();
EXPECT_EQ(initial_ihop, threshold);
// Whatever we pass, the IHOP value must stay the same.
test_update(&ctrl, &alloc_tracker, 2, 10, 10, 3);
threshold = ctrl.get_conc_mark_start_threshold();
threshold = ctrl.old_gen_threshold_for_conc_mark_start();
EXPECT_EQ(initial_ihop, threshold);
test_update(&ctrl, &alloc_tracker, 12, 10, 10, 3);
threshold = ctrl.get_conc_mark_start_threshold();
threshold = ctrl.old_gen_threshold_for_conc_mark_start();
EXPECT_EQ(initial_ihop, threshold);
}
@ -126,23 +126,23 @@ TEST_VM(G1IHOPControl, adaptive_simple) {
- (young_size + alloc_amount1 / alloc_time1 * marking_time1);
size_t threshold;
threshold = ctrl.get_conc_mark_start_threshold();
threshold = ctrl.old_gen_threshold_for_conc_mark_start();
EXPECT_EQ(initial_threshold, threshold);
for (size_t i = 0; i < G1AdaptiveIHOPNumInitialSamples - 1; i++) {
test_update_allocation_tracker(&alloc_tracker, alloc_amount1);
ctrl.update_allocation_info(alloc_time1, young_size);
ctrl.update_marking_length(marking_time1);
ctrl.add_marking_start_to_mixed_length(marking_time1);
// Not enough data yet.
threshold = ctrl.get_conc_mark_start_threshold();
threshold = ctrl.old_gen_threshold_for_conc_mark_start();
ASSERT_EQ(initial_threshold, threshold) << "on step " << i;
}
test_update(&ctrl, &alloc_tracker, alloc_time1, alloc_amount1, young_size, marking_time1);
threshold = ctrl.get_conc_mark_start_threshold();
threshold = ctrl.old_gen_threshold_for_conc_mark_start();
EXPECT_EQ(settled_ihop1, threshold);
@ -155,7 +155,7 @@ TEST_VM(G1IHOPControl, adaptive_simple) {
test_update(&ctrl, &alloc_tracker, alloc_time2, alloc_amount2, young_size, marking_time2);
threshold = ctrl.get_conc_mark_start_threshold();
threshold = ctrl.old_gen_threshold_for_conc_mark_start();
EXPECT_LT(threshold, settled_ihop1);
@ -166,14 +166,14 @@ TEST_VM(G1IHOPControl, adaptive_simple) {
const size_t settled_ihop3 = 0;
test_update(&ctrl, &alloc_tracker, alloc_time3, alloc_amount3, young_size, marking_time3);
threshold = ctrl.get_conc_mark_start_threshold();
threshold = ctrl.old_gen_threshold_for_conc_mark_start();
EXPECT_EQ(settled_ihop3, threshold);
// And back to some arbitrary value.
test_update(&ctrl, &alloc_tracker, alloc_time2, alloc_amount2, young_size, marking_time2);
threshold = ctrl.get_conc_mark_start_threshold();
threshold = ctrl.old_gen_threshold_for_conc_mark_start();
EXPECT_GT(threshold, settled_ihop3);
}
@ -205,7 +205,7 @@ TEST_VM(G1IHOPControl, adaptive_humongous) {
humongous_bytes_after_last_gc, young_size, marking_time);
// Test threshold
size_t threshold;
threshold = ctrl.get_conc_mark_start_threshold();
threshold = ctrl.old_gen_threshold_for_conc_mark_start();
// Adjusted allocated bytes:
// Total bytes: humongous_bytes
// Freed hum bytes: humongous_bytes - humongous_bytes_after_last_gc
@ -219,7 +219,7 @@ TEST_VM(G1IHOPControl, adaptive_humongous) {
ctrl2.update_target_occupancy(target_size);
test_update_humongous(&ctrl2, &alloc_tracker, duration, old_bytes, humongous_bytes,
humongous_bytes_after_gc, young_size, marking_time);
threshold = ctrl2.get_conc_mark_start_threshold();
threshold = ctrl2.old_gen_threshold_for_conc_mark_start();
// Adjusted allocated bytes:
// Total bytes: old_bytes + humongous_bytes
// Freed hum bytes: humongous_bytes - (humongous_bytes_after_gc - humongous_bytes_after_last_gc)
@ -235,7 +235,7 @@ TEST_VM(G1IHOPControl, adaptive_humongous) {
ctrl3.update_target_occupancy(target_size);
test_update_humongous(&ctrl3, &alloc_tracker, duration, old_bytes, humongous_bytes,
humongous_bytes_after_gc, young_size, marking_time);
threshold = ctrl3.get_conc_mark_start_threshold();
threshold = ctrl3.old_gen_threshold_for_conc_mark_start();
// Adjusted allocated bytes:
// All humongous are cleaned up since humongous_bytes_after_gc < humongous_bytes_after_last_gc
// Total bytes: old_bytes + humongous_bytes