类文件格式是否支持 final 关键字,以便将其与变量一起使用?
或者它只是从代码中推断出变量的有效终结性,而JIT编译器是否基于它执行优化?
Here,在类文件格式文档中,他们提到了final关键字,但仅限于将其与 final block 和 final class 一起使用。<登记/> 最终变量没有任何内容。
答案 0 :(得分:7)
不,在类文件中没有编码此类信息。
您可以通过使用final
局部变量并且没有final
编译源文件来轻松验证这一点 - 结果类将是相同的。
但是,Java 8添加了MethodParameters
属性,该属性记录有关方法参数的名称和访问标志的信息。这意味着,您可以检查方法参数是否为final
。
即时编译器不需要了解final
本地人 - 他们可以轻松确定任何表达式的实际范围。即使变量不是最终的,例如
int x = 1;
// ... code A ...
x = 2;
// ... code B ...
编译器会优化代码A
,就好像x
始终是1
一样,代码B
就好像x
始终是2
}。
答案 1 :(得分:2)
也许我们应该首先重新考虑“变量”一词。在大多数情况下,术语“变量”包括局部变量,static
和非static
字段,甚至通常包括数组元素(例如,在内存模型中)。由于数组元素不支持final
,因此只能为字段和局部变量给出答案。
对于字段,是 ACC_FINAL
标志,用于指示字段是否为final
。它有不同的后果。 static final
字段只能在类初始值设定项中写入,而final
实例字段不仅可以在构造函数中写入,还可以通过带有访问覆盖的反射。在优化时,JVM试图从实例字段的final
性质中获益,必须注意检测反射修改。
对于局部变量,没有final
标志,事实上,根本没有正式的声明。在Java字节代码中,局部变量只是stack frame中的索引,可以随意重用而不会预感。因此,对局部变量索引的写入可以是变量的变化,也可以是对新变量的相同索引的重用,例如, { int x=4; } { int y=5; }
可能会编译为与{ int x=4; x=5; }
相同的字节代码。
对于JVM的优化器,无论如何都没关系,因为它会将局部变量的操作转换为SSA form,因此,通过上面的示例,优化器会将代码视为必须为常量, c₁:=4
和c₂:=5
,并且根据后续代码的位置,可以确定使用哪个常量,换句话说,它不仅仅具有“有效最终”变量,甚至可以处理变更变量像多个final
变量一样(在没有线程同步的情况下,即使对堆变量的更改也可能暂时得到类似的处理)。