If code tries to set a flag with a value that violates its constraint, the setting will be ignored and no warning is reported.
This can be seen by applying the following patch:
diff --git a/src/hotspot/share/runtime/flags/jvmFlag.cpp b/src/hotspot/share/runtime/flags/jvmFlag.cpp
--- a/src/hotspot/share/runtime/flags/jvmFlag.cpp
+++ b/src/hotspot/share/runtime/flags/jvmFlag.cpp
@@ -1444,7 +1444,7 @@
}
void JVMFlag::printError(bool verbose, const char* msg, ...) {
- if (verbose) {
+ if (true) {
va_list listPointer;
va_start(listPointer, msg);
jio_vfprintf(defaultStream::error_stream(), msg, listPointer);
And running:
test/hotspot/jtreg/runtime/CommandLine/OptionsValidation
The failing test shows that with the following command line:
$ ../build/release/images/jdk/bin/java -XX:+UseG1GC -XX:+UnlockDiagnosticVMOptions -XX:SharedArchiveFile=TestOptionsWithRanges.jsa -Xshare:dump -XX:SharedBaseAddress=0
We try to set NonNMethodCodeHeapSize to 0:
uintx NonNMethodCodeHeapSize=0 is outside the allowed range [ 4096 ... 18446744073709551615 ]
And if we check -XX:+PrintFlagsFinal | grep NonNMethodCodeHeapSize we see that the flag has not been set:
uintx NonNMethodCodeHeapSize=0 is outside the allowed range [ 4096 ... 18446744073709551615 ]
uintx NonNMethodCodeHeapSize = 5242880 {pd product} {default}
The code that tries to set the flag to a value outside its range is here:
if (SegmentedCodeCache) {
// Use multiple code heaps
initialize_heaps();
} else {
// Use a single code heap
FLAG_SET_ERGO(NonNMethodCodeHeapSize, 0);
FLAG_SET_ERGO(ProfiledCodeHeapSize, 0);
FLAG_SET_ERGO(NonProfiledCodeHeapSize, 0);
ReservedCodeSpace rs = reserve_heap_memory(ReservedCodeCacheSize);
add_heap(rs, "CodeCache", CodeBlobType::All);
}
The code that swallows the warning is here:
static JVMFlag::Error apply_constraint_and_check_range_size_t(const JVMFlag* flag, size_t new_value, bool verbose) {
JVMFlag::Error status = JVMFlag::SUCCESS;
JVMFlagRange* range = JVMFlagRangeList::find(flag);
if (range != NULL) {
status = range->check_size_t(new_value, verbose);
}
if (status == JVMFlag::SUCCESS) {
JVMFlagConstraint* constraint = JVMFlagConstraintList::find_if_needs_check(flag);
if (constraint != NULL) {
status = constraint->apply_size_t(new_value, verbose);
}
}
return status;
}
JVMFlag::Error JVMFlag::size_tAtPut(JVMFlag* flag, size_t* value, JVMFlag::Flags origin) {
if (flag == NULL) return JVMFlag::INVALID_FLAG;
if (!flag->is_size_t()) return JVMFlag::WRONG_FORMAT;
JVMFlag::Error check = apply_constraint_and_check_range_size_t(flag, *value, !JVMFlagConstraintList::validated_after_ergo());
if (check != JVMFlag::SUCCESS) return check;
size_t old_value = flag->get_size_t();
trace_flag_changed<EventUnsignedLongFlagChanged, u8>(flag, old_value, *value, origin);
check = flag->set_size_t(*value);
*value = old_value;
flag->set_origin(origin);
return check;
}
!JVMFlagConstraintList::validated_after_ergo() starts to return false in the middle of the JVM initialization. This value is passed to the verbose parameter, and check_size_t(...) calls printError(...) with verbose set to false
void JVMFlag::printError(bool verbose, const char* msg, ...) {
if (verbose) {
va_list listPointer;
va_start(listPointer, msg);
jio_vfprintf(defaultStream::error_stream(), msg, listPointer);
va_end(listPointer);
}
}
Since the check returned an error size_tAtPut returns at this line:
if (check != JVMFlag::SUCCESS) return check;
and the setting of the flag is silently ignored.
If found one problematic instance of this, maybe there are more?