跳到主要内容

JEP 245:验证 JVM 命令行标志参数

QWen Max 中英对照

概述

验证所有 JVM 命令行标志的参数,以避免崩溃,并确保在参数无效时显示适当的错误消息。

非目标

  • 我们不会验证那些未被 JVM 处理的标志参数。

  • 我们不会尝试将参数调整到允许的范围内;我们只会检测不正确的参数,而不会修正它们。

成功指标

任何接受值的 JVM 参数如果被赋予了超出范围的值,都不会导致 JVM 崩溃,而是会发出一条信息性错误消息。无论这些 JVM 参数是通过人体工程学设置(例如,在包含文件中)、命令行设置、工具输入设置,还是通过 API 设置(例如,attachListener/jcmdjava.lang.management),都会执行这一操作。

描述

任何接口,无论是程序性的还是用户可见的,都必须提供足够的能力来验证输入值。对于命令行来说,对需要用户指定值的参数实施范围检查是至关重要的。globals.hpp 源文件包含了标志值的来源和基本的范围检查。扩展和完善这一功能可以提供正确的覆盖范围。

此外,我们应该定义一个框架,使得添加新的 JVM 命令行标志的人能够非常容易地利用这种有效性检查。该框架应具有灵活性,允许检查单个值、介于最小值和最大值之间,或处于一组值之内等。

我们将通过扩展现有的宏表(例如 RUNTIME_FLAGS)来实现此功能,新增可选的 range(min, max)constraint(function_pointer) 条目。当前的范围检查及其他临时验证代码将被移植过来,然后移除。

范围和约束检查会在每次标志(flag)更改时执行,同时在 JVM 初始化例程的后期(即,在 stubRoutines_init2() 之后的 init_globals() 中)进行,此时所有标志都已设置为最终值。只要 JVM 运行,我们将继续检查可管理的标志(manageable flags)。

对于那些依赖于其他可能在设置相关标志时未被设置的标志,我们将提供一种机制,以 API(CommandLineFlags::finishedInitializing())的形式,让约束函数知道何时所有标志都已设置为其最终值,并根据需要将其行为从无操作(NOP)更改为错误。

拦截标志值的更改是在 CommandLineFlags::xxxxAtPut 设置器中以较低级别完成的,以确保可管理的标志检查其范围和约束条件(例如,通过 jcmd 设置的标志)。例如,使用 jcmd PID VM.set_flag MinHeapFreeRatio 101,该值超出了允许的范围,将打印出相关信息。

PID:
MinHeapFreeRatio error: must have value in range [0...100]

jcmd 输出中。

范围检查不会对 JVM 初始化过程施加任何行为更改。特别是它们不会终止 JVM,而是将其状态传播到使用它们的代码。约束函数可以根据需要终止 JVM,以匹配现有的自定义行为。

范围/约束检查默认是非冗长的,以抑制其消息的输出。范围检查在 JVM 初始化期间会在错误流中打印错误消息,以匹配当前行为。对于可管理标志,抑制向错误流的打印操作;任何错误状态将由 WriteableFlags 代码处理,以将详细状态提供到指定的 FormatBuffer 中,这样就可以通过 jcmd 进程本身而不是目标进程来打印这些信息。

在 JVM 初始化期间发生范围检查失败时,默认情况下会以下列形式打印错误消息:

uintx UnguardOnExecutionViolation = 3 is outside the allowed range [ 0 ... 2 ]

但是,我们目前不会承诺任何特定的格式。 期望某种消息格式的现有测试将必须进行修改,以允许使用新格式。

现有行为没有改变,即不会将标志限制在指定范围内(即,我们不会进行限制),尽管我们有这种能力。在初始化 JVM 期间进行错误检测时,我们遵循现有行为(即,我们终止进程)。

替代方案

可变参数宏提供了一种略有不同且可能更简洁的方式来定义范围和约束,但是 Solaris C++ 编译器中的“空参数的尾随逗号”问题阻碍了这种做法的采用。