7
class Foo {
  final val pi = 3
}

每个Foo对象都有一个pi成员吗?因此,我应该放入pi伴随对象吗?

4

2 回答 2

4

如果您担心内存占用,您可以考虑将此字段移动到伴随对象中。

是的,每个类的实例Foo都会有pi值——Scala 编译器不会消除这个声明。JVM 反射允许您删除类成员上的最终修饰符,并且Unsafe对象甚至允许修改这些修饰符。所以 - Scala 编译器可以通过删除该字段来生成具有令人惊讶的结果的代码,因此不应用此优化。

...
  minor version: 0
  major version: 50
  flags: ACC_PUBLIC, ACC_SUPER
...
{
  private final int pi;
    flags: ACC_PRIVATE, ACC_FINAL


  public final int pi();
    flags: ACC_PUBLIC, ACC_FINAL
    LineNumberTable:
      line 243: 0
    LocalVariableTable:
      Start  Length  Slot  Name   Signature
...

事实上,一些编译器转换(例如特化)甚至可能在底层移除成员的 final 修饰符,因此finalScala 代码中的某些东西可能不在final字节码级别。

这:

class Foo[@specialized T] {
  final val pi: T = null.asInstanceOf[T]
}

变成:

  ...
  public final T pi;
    flags: ACC_PUBLIC, ACC_FINAL
    Signature: #9                           // TT;


  public T pi();
    flags: ACC_PUBLIC
    LineNumberTable:
      line 243: 0
   ...

上面,pi访问器方法(即它的getter)不再是最终的。

Oracle JVM 中的 JIT 也不会在运行时从内存中的对象表示中删除该成员 - Foo32 位 JVM 上对象的运行时大小将为 16 字节(8 字节对象头 + 4 字节用于整数字段,舍入到 8 字节边界)。然而,JIT 可能会决定将最终字段中的常量值内联到部分代码中,从而消除一些字段写入。

于 2013-07-12T13:42:39.340 回答
3

不仅每个实例都有一个 field pi,而且它的值为零。

pi是一个常数值定义。“访问器”只返回常量。

如果您足够努力,这可能会在单独编译和内联的情况下导致问题。

{
  private final int pi;
    flags: ACC_PRIVATE, ACC_FINAL

  public final int pi();
    flags: ACC_PUBLIC, ACC_FINAL
    Code:
      stack=1, locals=1, args_size=1
         0: iconst_3      
         1: ireturn       
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
               0       2     0  this   LFoo;
      LineNumberTable:
        line 8: 0

  public Foo();
    flags: ACC_PUBLIC
    Code:
      stack=1, locals=1, args_size=1
         0: aload_0       
         1: invokespecial #14                 // Method java/lang/Object."<init>":()V
         4: return        
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
               0       5     0  this   LFoo;
      LineNumberTable:
        line 13: 0
}

只是为了说服自己,经过反思:

scala> res5.tail
res16: Iterable[reflect.runtime.universe.Symbol] = List(value pi)

scala> res5.last.asTerm.isAccessor
res18: Boolean = false

scala> res5.head.asTerm.isAccessor
res19: Boolean = true

scala> res0 reflectField res5.last.asTerm
res21: reflect.runtime.universe.FieldMirror = field mirror for Foo.pi (bound to Foo@2907f26d)

scala> res21.get
res22: Any = 0
于 2013-07-12T14:16:46.823 回答