8352969: G1: Improve testability of optional collections

Reviewed-by: ayang, tschatzl
This commit is contained in:
Guanqiang Han 2025-09-11 15:08:42 +00:00 committed by Albert Mingkun Yang
parent 7603e96fa2
commit e172e6a4e3
5 changed files with 156 additions and 10 deletions

View File

@ -385,6 +385,16 @@ static void print_finish_message(const char* reason, bool from_marking) {
from_marking ? "marking" : "retained", reason);
}
void G1CollectionSet::add_optional_group(G1CSetCandidateGroup* group,
uint& num_optional_regions,
double& predicted_optional_time_ms,
double predicted_time_ms) {
_optional_groups.append(group);
prepare_optional_group(group, num_optional_regions);
num_optional_regions += group->length();
predicted_optional_time_ms += predicted_time_ms;
}
double G1CollectionSet::select_candidates_from_marking(double time_remaining_ms) {
uint num_expensive_regions = 0;
uint num_inital_regions = 0;
@ -404,6 +414,8 @@ double G1CollectionSet::select_candidates_from_marking(double time_remaining_ms)
G1CSetCandidateGroupList* from_marking_groups = &candidates()->from_marking_groups();
bool make_first_group_optional = G1ForceOptionalEvacuation;
log_debug(gc, ergo, cset)("Start adding marking candidates to collection set. "
"Min %u regions, max %u regions, available %u regions (%u groups), "
"time remaining %1.2fms, optional threshold %1.2fms",
@ -421,6 +433,15 @@ double G1CollectionSet::select_candidates_from_marking(double time_remaining_ms)
double predicted_time_ms = group->predict_group_total_time_ms();
if (make_first_group_optional) {
make_first_group_optional = false;
add_optional_group(group,
num_optional_regions,
predicted_optional_time_ms,
predicted_time_ms);
continue;
}
time_remaining_ms = MAX2(time_remaining_ms - predicted_time_ms, 0.0);
// Add regions to old set until we reach the minimum amount
if (num_inital_regions < min_old_cset_length) {
@ -456,10 +477,10 @@ double G1CollectionSet::select_candidates_from_marking(double time_remaining_ms)
} else if (time_remaining_ms > 0) {
// Keep adding optional regions until time is up.
_optional_groups.append(group);
prepare_optional_group(group, num_optional_regions);
num_optional_regions += group->length();
predicted_optional_time_ms += predicted_time_ms;
add_optional_group(group,
num_optional_regions,
predicted_optional_time_ms,
predicted_time_ms);
} else {
print_finish_message("Predicted time too high", true);
break;
@ -560,10 +581,10 @@ void G1CollectionSet::select_candidates_from_retained(double time_remaining_ms)
num_initial_regions += group->length();
} else if (predicted_time_ms <= optional_time_remaining_ms) {
// Prepare optional collection region.
_optional_groups.append(group);
prepare_optional_group(group, num_optional_regions);
num_optional_regions += group->length();
predicted_optional_time_ms += predicted_time_ms;
add_optional_group(group,
num_optional_regions,
predicted_optional_time_ms,
predicted_time_ms);
} else {
// Fits neither initial nor optional time limit. Exit.
break;
@ -645,6 +666,7 @@ uint G1CollectionSet::select_optional_groups(double time_remaining_ms) {
log_debug(gc, ergo, cset)("Prepared %u regions out of %u for optional evacuation. Total predicted time: %.3fms",
num_regions_selected, optional_regions_count, total_prediction_ms);
return num_regions_selected;
}

View File

@ -220,6 +220,13 @@ class G1CollectionSet {
size_t offset,
size_t length,
uint worker_id) const;
// Adds the given group to the optional groups list (_optional_groups)
// and updates all related bookkeeping.
void add_optional_group(G1CSetCandidateGroup* group,
uint& num_optional_regions,
double& predicted_optional_time_ms,
double predicted_time_ms);
public:
G1CollectionSet(G1CollectedHeap* g1h, G1Policy* policy);
~G1CollectionSet();

View File

@ -815,13 +815,18 @@ void G1YoungCollector::evacuate_next_optional_regions(G1ParScanThreadStateSet* p
void G1YoungCollector::evacuate_optional_collection_set(G1ParScanThreadStateSet* per_thread_states) {
const double pause_start_time_ms = policy()->cur_pause_start_sec() * 1000.0;
double target_pause_time_ms = MaxGCPauseMillis;
if (G1ForceOptionalEvacuation) {
target_pause_time_ms = DBL_MAX;
}
while (!evacuation_alloc_failed() && collection_set()->num_optional_regions() > 0) {
double time_used_ms = os::elapsedTime() * 1000.0 - pause_start_time_ms;
double time_left_ms = MaxGCPauseMillis - time_used_ms;
double time_left_ms = target_pause_time_ms - time_used_ms;
if (time_left_ms < 0 ||
if (time_left_ms <= 0 ||
!collection_set()->finalize_optional_for_evacuation(time_left_ms * policy()->optional_evacuation_fraction())) {
log_trace(gc, ergo, cset)("Skipping evacuation of %u optional regions, no more regions can be evacuated in %.3fms",
collection_set()->num_optional_regions(), time_left_ms);

View File

@ -370,6 +370,12 @@
"scan cost related prediction samples. A sample must involve " \
"the same or more than this number of code roots to be used.") \
\
develop(bool, G1ForceOptionalEvacuation, false, \
"Force optional evacuation for all GCs where there are old gen " \
"collection set candidates." \
"Also schedule all available optional groups for evacuation " \
"regardless of timing.") \
\
GC_G1_EVACUATION_FAILURE_FLAGS(develop, \
develop_pd, \
product, \

View File

@ -0,0 +1,106 @@
/*
* Copyright (c) 2025, 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
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/* @test
* @bug 8352969
* @summary Tests optional evacuation.
* @requires vm.gc.G1
* @requires vm.debug
* @library /test/lib
* @build jdk.test.whitebox.WhiteBox
* @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox
* @run driver gc.g1.TestOptionalRegionGC
*/
package gc.g1;
import jdk.test.lib.Asserts;
import jdk.test.lib.process.OutputAnalyzer;
import jdk.test.lib.process.ProcessTools;
import jdk.test.whitebox.WhiteBox;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.Random;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class TestOptionalRegionGC {
private static OutputAnalyzer run() throws Exception {
return ProcessTools.executeLimitedTestJava(
"-XX:+WhiteBoxAPI",
"-Xbootclasspath/a:.",
"-Xmx300M",
"-Xms300M",
"-XX:G1HeapRegionSize=1M",
"-XX:+UseG1GC",
"-XX:MaxTenuringThreshold=1",
"-Xlog:gc+ergo+cset=trace",
"-XX:+G1ForceOptionalEvacuation",
"-XX:+VerifyAfterGC",
TestOptionalRegionGC.Action.class.getName());
}
public static void main(String args[]) throws Exception {
OutputAnalyzer out = run();
out.shouldHaveExitValue(0);
Pattern pattern = Pattern.compile("Prepared (\\d+) regions out of (\\d+) for optional evacuation");
Matcher matcher = pattern.matcher(out.getOutput());
Asserts.assertTrue(matcher.find());
String selectedNum = matcher.group(1);
String totalNum = matcher.group(2);
Asserts.assertTrue(Objects.equals(selectedNum, totalNum), "Error info: " + selectedNum + ", " + totalNum);
}
public static class Action {
private static final WhiteBox wb = WhiteBox.getWhiteBox();
private static final int MIN_OBJECT_SIZE = 64 * 1024;
private static final int MAX_OBJECT_SIZE = 120 * 1024;
private static final int NUM_OBJECTS = 1200;
public static void main(String [] args) throws Exception {
// Remove garbage from VM initialization.
wb.fullGC();
Random rand = new Random(42);
List<byte[]> objectList = new ArrayList<>();
for (int i = 0; i < NUM_OBJECTS; i++) {
int objSize = MIN_OBJECT_SIZE + rand.nextInt(MAX_OBJECT_SIZE - MIN_OBJECT_SIZE);
byte[] obj = new byte[objSize];
objectList.add(obj);
}
// Young GC promotes some objects to the old generation.
wb.youngGC();
// Clear certain references for mixed GC.
for (int i = 0; i < NUM_OBJECTS; i+=2) {
objectList.set(i, null);
}
wb.g1RunConcurrentGC();
// Perform the "Prepare Mixed" GC.
wb.youngGC();
// Perform the "Mixed" GC.
wb.youngGC();
}
}
}