Search code examples
scalaconstantsfinalcompile-time-constantcompanion-object

Do final vals increase the object size?


class Foo {
  final val pi = 3
}

Does every Foo object have a pi member? Should I therefore put pi in the companion object?


Solution

  • If you are concerned about the memory footprint, you might consider moving this field into the companion object.

    Yes, every instance of class Foo will have the pi value -- the Scala compiler will not eliminate this declaration. The JVM reflection allows you to remove final modifiers on class members, and the Unsafe object even allows modifying these. So -- the Scala compiler could produce code with surprising results by removing this field, so this optimization is not applied.

    ...
      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
    ...
    

    In fact, some compiler transformations (e.g. specialization) might even remove final modifiers on members under-the-hood, so something that feels final in Scala code might not be final on the bytecode level.

    This:

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

    becomes:

      ...
      public final T pi;
        flags: ACC_PUBLIC, ACC_FINAL
        Signature: #9                           // TT;
    
    
      public T pi();
        flags: ACC_PUBLIC
        LineNumberTable:
          line 243: 0
       ...
    

    Above, the pi accessor method (i.e. its getter) is no longer final.

    And neither will the JIT in the Oracle JVM remove this member from the object representation in memory at runtime - the runtime size of the Foo object on a 32-bit JVM will be 16 bytes (8 bytes object header + 4 bytes for an integer field, rounded to an 8 byte boundary). The JIT might, however, decide to inline the constant value from the final field into parts of the code, so that some field writes are eliminated.