Skip to content
Prev Previous commit
Next Next commit
Attempt to fix the JvmOptions in native-image
  • Loading branch information
jbachorik committed Jul 23, 2025
commit 585781d1534c28de4f352ed375fca10505b54804
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;
import java.util.ListIterator;
import java.util.StringTokenizer;

/** Fetches and captures the JVM options. */
Expand All @@ -41,28 +40,35 @@ private String[] readProcFsCmdLine() {
return null;
}

@SuppressForbidden // Class.forName() as backup
private List<String> findVmOptions() {
return findVmOptions(PROCFS_CMDLINE);
}

@SuppressForbidden // Class.forName() as backup
// Visible for testing
List<String> findVmOptions(String[] procfsCmdline) {
// Try ProcFS on Linux
if (PROCFS_CMDLINE != null) {
// Be aware that when running a native image, the command line in /proc/self/cmdline is just the
// executable
if (procfsCmdline != null) {
// Create list of VM options
List<String> vmOptions = new ArrayList<>();
// Start at 1 to skip "java" command itself
int index = 1;
// Look for main class or "-jar", end of VM options
for (; index < PROCFS_CMDLINE.length; index++) {
if (!PROCFS_CMDLINE[index].startsWith("-") || "-jar".equals(PROCFS_CMDLINE[index])) {
break;
// Simultaneously, collect all arguments in the VM options
for (; index < procfsCmdline.length; index++) {
String argument = procfsCmdline[index];
if (argument.startsWith("@")) {
vmOptions.addAll(getArgumentsFromFile(argument));
} else {
vmOptions.add(argument);
}
}
// Create list of VM options
List<String> vmOptions = new ArrayList<>(asList(PROCFS_CMDLINE).subList(1, index + 1));
ListIterator<String> iterator = vmOptions.listIterator();
while (iterator.hasNext()) {
String vmOption = iterator.next();
if (vmOption.startsWith("@")) {
iterator.remove();
for (String argument : getArgumentsFromFile(vmOption)) {
iterator.add(argument);
if (!argument.startsWith("-") || "-jar".equals(argument)) {
if (index + 1 < procfsCmdline.length) {
vmOptions.add(procfsCmdline[index + 1]); // jar file or the main class
}
break;
}
}
// Insert JDK_JAVA_OPTIONS at the start if present and supported
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,42 @@ void testFindVmOptions(
assertEquals(expectedArguments.jvmOptions, result.jvmOptions, "Failed to get JVM options");
}

@MethodSource
private static Stream<Arguments> procFsCmdLine() {
// spotless:off
return Stream.of(
arguments(
"No arguments",
new String[0],
emptyList()
),
arguments(
"Native image launcher",
new String[]{"native-image-launcher", "-Xmx512m"},
singletonList("-Xmx512m")
),
arguments(
"Java with JAR and options",
new String[]{"java", "-Xmx512m", "-Xms256m", "-jar", "app.jar"},
asList("-Xmx512m", "-Xms256m", "-jar", "app.jar")
),
arguments(
"Java from class and options",
new String[]{"java", "-Xmx512m", "-Xms256m", "-cp", "app.jar", "Main"},
asList("-Xmx512m", "-Xms256m", "-cp", "app.jar", "Main")
));
// spotless:on
}

@ParameterizedTest(name = "[{index}] {0}")
@MethodSource("procFsCmdLine")
void testFindVmOptionsWithProcFsCmdLine(
String useCase, String[] procfsCmdline, List<String> expected) throws Exception {
JvmOptions vmOptions = new JvmOptions();
List<String> found = vmOptions.findVmOptions(procfsCmdline);
assertEquals(expected, found);
}

private void skipJdkJavaOptionsOnJava8(Map<String, String> environmentVariables) {
assumeTrue(
JavaVirtualMachine.isJavaVersionAtLeast(9)
Expand Down
Loading