Long、Double、または Boolean のプリミティブ値 (および簡単にするために削除したその他のもの) を格納できるラッパー クラスがあります。私の最初の素朴な実装では、ラップされた値を Any 型のフィールドに格納しただけで、値がボックス化されました。
ボクシングを削除してメモリ使用量を減らすために、ジェネリックを使用してみましたが、型消去のために何も保存されないことがわかりました。そこで @specialized を使ってみたのですが、驚くべき結果が得られました。
以下のコードは scalac 2.9.3 でビルドされ、JDK7 で実行されます。MemoryMeasurer はここから来ており、正確であると信じています。「パディング」フィールドは重要ではありません。基本オブジェクト (ラップされた値なし) を 16 バイトにパディングするために使用しているだけなので、さまざまな試行の効果がより明確になります。
import objectexplorer.MemoryMeasurer
class GenericNonSpecialized[A] (wrapped: A, val padding: Int) {
def getWrapped: Any = wrapped
}
class GenericSpecialized[@specialized(Long, Double, Boolean) A] (wrapped: A, val padding: Int) {
def getWrapped: A = wrapped
}
class GenericSpecializedVal[@specialized(Long, Double, Boolean) A] (val wrapped: A, val padding: Int) {
def getWrapped: A = wrapped
}
class NonGeneric(val wrapped: Long, padding: Int) {
}
object App {
def main(args: Array[String]) {
println(MemoryMeasurer.measureBytes(new GenericNonSpecialized(4L, 0)))
// Expect: 48: NonSpecialized object (24 bytes) + boxed long (24 bytes)
// Actual: 48
// I expect all of the below to be 24 bytes: Object overhead (12 bytes) + Long (8 bytes) + Int (4 bytes),
// but only the non-generic one is actually 24 bytes.
println(MemoryMeasurer.measureBytes(new GenericSpecialized(4L, 0))) // 56
println(MemoryMeasurer.measureBytes(new GenericSpecializedVal(4L, 0))) // 32
println(MemoryMeasurer.measureBytes(new NonGeneric(4L, 0))) // 24
}
}
質問:
- 非ジェネリック版のように、24 バイトを使用するジェネリック ラッパー オブジェクトを作成するにはどうすればよいですか? (私の最善の試み、GenericSpecializedVal は 32 を使用)
- GenericNonSpecialized が 56 バイトを使用するのはなぜですか?「val」を追加して「ラップ」を実際のフィールドにすると、32 バイトに減少します。