最终变量是否在每个实例的基础上占用内存?

Does final variables occupy memory on a per-instance basis?

我已经阅读了有关最终实例变量的其他问答,我开始知道 非静态最终实例变量是在堆中为 [=22] 的每个实例创建的=] 但在 java - herbert schildt 的完整参考中据说:

Variables declared as final do not occupy memory on a per-instance basis .thus, a final variable is essentially a constant

哪个是正确的?

第一个陈述是正确的...虽然仅限于 final 个实例字段。

第二个说法不正确。

一个final变量会在运行时某处占用内存。但是,该内存的位置取决于您声明为 final.

的变量类型
  • 如果变量是局部变量或方法的形式参数变量,则内存单元将在堆栈上。

  • 如果变量是非静态字段,则内存单元将是对象的一部分,并在堆中。

  • 如果变量是静态字段,则内存单元的位置将取决于实现。 (它可能在堆中,也可能在其他地方。)

需要注意的是,一些(但不是全部!)类型的 static final 字段是编译常量,它们的值被编译器有效地内联。但即使发生这种情况,在运行时仍然会有一个实际字段占用一个内存单元,并且可以反射地访问该内存单元的内容。

(事实上,您甚至可以反射性地修改 final 字段。当您这样做时,JVM 规范未指定该行为,并且在某些情况下可能相当令人惊讶。)


Does final variables occupy memory on a per-instance basis?

不,因为:

  • 静态变量不 "occupy memory on a per-instance" 无论它们是否 final
  • 局部变量和参数也可以是final,但不能"occupy memory on a per-instance"

Can this assumption be made by the compiler if final variables are initialized at their declaration itself and allocate memory to it only one time because obviously every instance created will have the same value for that final variable. Is compiler smart enough to do this kind of optimization?

考虑一下

public class Test
    public static test(final int val) {
        System.out.println(val);
    }
}

现在 valfinal,但它的值取决于您如何调用 test 方法。这在 (javac) 编译时是未知的,通常 JIT 编译器也不知道。事实上,假设 test 是用不同的参数调用的,你不能直接优化掉 val 内存单元。

除了符合编译时常量条件的 static final 变量(根据 JLS 定义)之外,这同样适用于所有情况。

从理论上讲,JIT 编译器可能会发现 Test.test 仅使用相同的常量值调用,并将该值内联到本机代码中,这样就不需要内存单元了。但是,该优化的性能优势很小,不太可能适用于实际代码。所以我怀疑值得实施它。

(由于将 test 的主体内联到其调用站点,val 更有可能被优化掉。这是一个更通用的优化,绝对值得。那可能会允许窥孔优化器推断内联代码中不需要 val。)