【问题标题】:Do final vals increase the object size?最终值会增加对象大小吗?
【发布时间】:2013-07-11 01:17:57
【问题描述】:
class Foo {
  final val pi = 3
}

每个Foo 对象都有pi 成员吗?因此我应该将pi 放在伴随对象中吗?

【问题讨论】:

    标签: scala constants final compile-time-constant companion-object


    【解决方案1】:

    不仅每个实例都有一个字段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
    

    【讨论】:

      【解决方案2】:

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

      是的,Foo 类的每个实例都将具有pi 值——Scala 编译器不会消除此声明。 JVM 反射允许您删除类成员上的 final 修饰符,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 修饰符,因此在 Scala 代码中感觉 final 的东西在字节码级别上可能不是 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 也不会在运行时从内存中的对象表示中删除该成员 - 32 位 JVM 上 Foo 对象的运行时大小将为 16 字节(8 字节对象头 + 4整数字段的字节,四舍五入到 8 字节边界)。然而,JIT 可能会决定将最终字段中的常量值内联到部分代码中,从而消除一些字段写入。

      【讨论】:

        猜你喜欢
        • 2014-08-24
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-03-23
        • 2022-06-14
        • 2020-06-08
        • 2016-01-24
        • 1970-01-01
        相关资源
        最近更新 更多