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