工具
使用了Unsafe内的staticFieldOffset(Field f),获取静态字段在内存地址中的偏移量
fun Class<*>.getSizeStatic() {
val unsafe: Unsafe = getUnsafe() ?: return
val field = declaredFields
println("$name----------------------------------Start")
field.forEach {
println("$name.${it.name}---offset: ${unsafe.staticFieldOffset(it)}")
}
println("$name----------------------------------End\n")
}
fun getUnsafe(): Unsafe? = try {
val field = Unsafe::class.java.getDeclaredField("theUnsafe").apply {
isAccessible = true
}
field.get(null) as Unsafe
} catch (e: Exception) {
println(e)
null
}
枚举
enum class TypeEnum {
ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN, EIGHT, NINE, TEN
}
结果:可以看出ONE和TWO的offset是4,也就是每个枚举占4个字节。但是因为会自动生成values这个枚举类数组,可以对比静态常量时会额外产生一定的内存开销。
TypeEnum----------------------------------Start
TypeEnum.ONE---offset: 112
TypeEnum.TWO---offset: 116
TypeEnum.THREE---offset: 120
TypeEnum.FOUR---offset: 124
TypeEnum.FIVE---offset: 128
TypeEnum.SIX---offset: 132
TypeEnum.SEVEN---offset: 136
TypeEnum.EIGHT---offset: 140
TypeEnum.NINE---offset: 144
TypeEnum.TEN---offset: 148
TypeEnum.$VALUES---offset: 152
TypeEnum----------------------------------End
密封类
sealed class TypeSealed {
object One: TypeSealed()
object Two: TypeSealed()
object Three: TypeSealed()
object Four: TypeSealed()
object Five: TypeSealed()
object Six: TypeSealed()
object Seven: TypeSealed()
object Eight: TypeSealed()
object Nine: TypeSealed()
object Ten: TypeSealed()
}
结果:密封类有趣的是每一个的偏移都是112,它们每一个都是一个对象,并且每一个的内部实现都是使用了单例。当我们使用
when(sealed) {}通过show kotlin byte code我们可以发现其实内部编译成了if(Intrinsics.areEqual(sealedType, SealedType.INSTANCE))。继续往内部深究发现areEuqal内部实现是return first == null ? second == null : first.equals(second);这里其实是比较了是否是同一个对象。
TypeSealed----------------------------------Start
TypeSealed$One---offset: 112
TypeSealed$Two---offset: 112
TypeSealed$Three---offset: 112
TypeSealed$Four---offset: 112
TypeSealed$Five---offset: 112
TypeSealed$Six---offset: 112
TypeSealed$Seven---offset: 112
TypeSealed$Eight---offset: 112
TypeSealed$Nine---offset: 112
TypeSealed$Ten---offset: 112
TypeSealed----------------------------------End
注解
@IntDef(value = [
TypeAnnotation.ONE, TypeAnnotation.TWO, TypeAnnotation.THREE,
TypeAnnotation.FOUR, TypeAnnotation.FIVE, TypeAnnotation.SIX,
TypeAnnotation.SEVEN, TypeAnnotation.EIGHT, TypeAnnotation.NINE,
TypeAnnotation.TEN
])
@Retention(AnnotationRetention.SOURCE)
annotation class TypeAnnotation {
companion object {
const val ONE = 1
const val TWO = 2
const val THREE = 3
const val FOUR = 4
const val FIVE = 5
const val SIX = 6
const val SEVEN = 7
const val EIGHT = 8
const val NINE = 9
const val TEN = 10
}
}
结果:注解方式的内存偏移量和静态常量对比会发现其实是一模一样的,同样也减少了枚举自动生成的values数组的开销,但是注解方式有个好处是,我们可以很简单的使用
@TypeAnnotiation value: Int去约束范围。
TypeAnnotation----------------------------------Start
TypeAnnotation.Companion---offset: 112
TypeAnnotation.one---offset: 116
TypeAnnotation.two---offset: 120
TypeAnnotation.three---offset: 124
TypeAnnotation.four---offset: 128
TypeAnnotation.five---offset: 132
TypeAnnotation.six---offset: 136
TypeAnnotation.seven---offset: 140
TypeAnnotation.eight---offset: 144
TypeAnnotation.nine---offset: 148
TypeAnnotation.ten---offset: 152
TypeAnnotation----------------------------------End
静态常量
class TypeClass {
companion object {
const val ONE = 1
const val TWO = 2
const val THREE = 3
const val FOUR = 4
const val FIVE = 5
const val SIX = 6
const val SEVEN = 7
const val EIGHT = 8
const val NINE = 9
const val TEN = 10
}
}
结果:其实不用去获取偏移量我们都知道静态常量是占4个字节的。静态常量同样减少了枚举类自动生成的values的开销。但是静态常量难以约束开发者使用时的范围,如果使用
@IntDef注解,单处使用还好,多处使用还是注解方式更好。
TypeClass----------------------------------Start
TypeClass.ONE---offset: 116
TypeClass.TWO---offset: 120
TypeClass.THREE---offset: 124
TypeClass.FOUR---offset: 128
TypeClass.FIVE---offset: 132
TypeClass.SIX---offset: 136
TypeClass.SEVEN---offset: 140
TypeClass.EIGHT---offset: 144
TypeClass.NINE---offset: 148
TypeClass.TEN---offset: 152
TypeClass.Companion---offset: 112
TypeClass----------------------------------End
结论
- 枚举类的内存占用是8n+24(4n+24为数组开销)。静态常量以及注解方式都为4n,当n足够的时候,确实枚举类是静态常量方式的2倍。综上所述,确实枚举类会产生额外的内存开销,但其实正常使用是完全没问题的,并且枚举类有其他特性——非连续数值的判断,重载等,另外,内存用量也并非那么地可怕,枚举带来的编码的便捷,代码可读性的提升也是很大的利好。
- 使用静态常量时,我更推荐使用注解方式代替它。
- 密封类会生成对个对象,我觉得应该适当减少使用。