我有一个包装类,可以存储 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 个字节?