class Foo {
final val pi = 3
}
Ogni oggetto Foo
ha un membro pi
? Dovrei quindi inserire pi
nell'oggetto associato?I valori valali finali aumentano la dimensione dell'oggetto?
class Foo {
final val pi = 3
}
Ogni oggetto Foo
ha un membro pi
? Dovrei quindi inserire pi
nell'oggetto associato?I valori valali finali aumentano la dimensione dell'oggetto?
Se si è preoccupati dell'impronta di memoria, si potrebbe prendere in considerazione lo spostamento di questo campo nell'oggetto associato.
Sì, ogni istanza della classe Foo
avrà il valore pi
- il compilatore Scala non eliminerà questa dichiarazione. La riflessione JVM consente di rimuovere i modificatori finali sui membri della classe e l'oggetto Unsafe
consente anche di modificarli. Quindi, il compilatore Scala potrebbe produrre codice con risultati sorprendenti rimuovendo questo campo, quindi questa ottimizzazione non viene applicata.
...
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
...
Infatti, alcune trasformazioni del compilatore (ad esempio specializzazione) potrebbe anche rimuovere i modificatori finali sui membri sotto il cofano, in modo da qualcosa che si sente final
in codice Scala potrebbe non essere final
a livello bytecode.
Questo:
class Foo[@specialized T] {
final val pi: T = null.asInstanceOf[T]
}
diventa:
...
public final T pi;
flags: ACC_PUBLIC, ACC_FINAL
Signature: #9 // TT;
public T pi();
flags: ACC_PUBLIC
LineNumberTable:
line 243: 0
...
Sopra, il metodo pi
di accesso (cioè il suo getter) non è più finale.
E nemmeno il JIT in Oracle JVM rimuove questo membro dalla rappresentazione oggetto in memoria in fase di esecuzione - la dimensione di runtime dell'oggetto Foo
su una JVM a 32 bit sarà di 16 byte (intestazione di oggetto 8 byte + 4 byte per un campo intero, arrotondato a un limite di 8 byte). Il JIT potrebbe, tuttavia, decidere di allineare il valore costante del campo finale in parti del codice, in modo che alcune scritture sul campo vengano eliminate.
Non solo ogni istanza avrà un campo pi
, avrà valore zero.
pi
è una definizione di valore costante. L '"accessor" restituisce solo la costante.
Questo può causare problemi in condizioni di compilazione e inlining separate, se ci provate abbastanza.
{
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
}
Solo per convincermi, riflettendoci:
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 [email protected])
scala> res21.get
res22: Any = 0