Skip to content

JEP 245: Validate JVM Command-Line Flag Arguments | 验证 JVM 命令行标志参数

摘要

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

非目标

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

  • 我们不会尝试调整参数使其落在允许范围内;我们只检测不正确的参数,而不进行修正。

成功指标

任何接受值并提供超出范围的值的 JVM 标志都将显示为不会导致 JVM 崩溃,而是发出信息性错误消息。这将适用于 JVM 标志,无论它们是通过人工设定(例如在包括文件中)、在命令行中设定、通过工具输入设定还是通过 API(例如 attachListener/jcmd 或者 java.lang.management)设定。

描述

任何接口,无论是以编程方式还是可见给用户,都必须提供足够的能力来验证输入值。对于命令行而言,实现对需要由用户指定值的参数进行范围检查至关重要。globals.hpp 源文件包含标志值的来源和基本的范围检查。通过扩展和完善,可以提供正确的覆盖。

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

我们将通过扩展现有的宏表(例如 RUNTIME_FLAGS)来实现这个特性,添加可选的 range(min, max)constraint(function_pointer) 条目。当前的范围检查和其他临时验证代码将被移植过来,然后删除。

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

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

拦截标志值变化是在 CommandLineFlags::xxxxAtPut 的低级 setter 中进行的,以确保可管理的标志被检查其范围和约束(例如通过 jcmd 设置的标志)。例如,使用 jcmd PID VM.set_flag MinHeapFreeRatio 101,这超出了允许的范围,将在 jcmd 输出中打印如下内容:

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

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

默认情况下,范围/约束检查是非冗余的,以阻止其消息被打印出来。在 JVM 初始化期间,范围检查会在错误流中打印错误消息,以匹配当前行为。对于可管理的标志,会禁止打印到错误流;任何错误状态将由 WriteableFlags 代码处理,使其提供详细的状态到提供的 FormatBuffer,以便它可以由 jcmd 进程本身而不是目标进程打印。

在 JVM 初始化过程中,如果范围检查失败,则默认情况下会打印错误消息,格式如下:

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

然而,我们目前并不承诺采用任何特定格式。对于那些期望特定消息格式的现有测试,需要进行修改以允许新的格式。

在关于将标志限制在指定范围内时,现有行为没有发生变化(即我们不进行限制),尽管我们具备这种能力。在初始化 JVM 时符合现有行为进行错误检测(即终止进程)。

可选方案

可变参数宏提供了一种稍微不同且可能更清晰的方式来定义范围和约束,但 Solaris C++ 编译器中的“带有空参数的尾逗号”问题阻止了采用这种方法。