Kotlin中各类常量声明方式的性能差异及开销对比
Kotlin常量声明的性能开销对比(仅聚焦性能)
咱们直接直奔主题,完全不讨论可读性、最佳实践这些,只拆解你列出的四种方案在性能和资源开销上的差异:
选项1:Companion Object + const val
先看编译后的Java代码:确实会生成一个Companion内部类,并且在Thing类加载时创建一个静态的Companion实例。但重点是——访问TAG的性能和Java静态常量完全一致:
const val是编译期常量,TAG会被编译成Thing类的直接静态final字段,访问时直接取内存地址,根本不需要经过Companion对象。- 唯一的额外开销是类加载时创建一次
Companion实例,这个开销微乎其微(一个空对象,占用内存极少,且只初始化一次),对运行时性能几乎没有影响。
选项2:Top-level const val
编译后生成一个ThingKt类,里面包含静态final的TAG字段:
- 访问
TAG就是直接访问静态编译期常量,和Java的static final完全一样,没有任何额外对象创建。 - 唯一的“开销”是多了一个
ThingKt类,但JVM类加载的成本极低,且这个类只包含静态常量,内存占用可以忽略。 - 这是性能最极致的方案之一,没有多余的对象实例,访问速度最快。
选项3:External Object + const val
编译后生成的ThingConstants类有一个静态INSTANCE实例(类加载时初始化一次),同时TAG是直接的静态final字段:
- 和选项2一样,
const val TAG是编译期常量,访问时直接取静态字段,完全不需要通过INSTANCE对象。 - 额外开销仅为类加载时创建的
INSTANCE空对象,和选项1的Companion实例一样,对性能无实际影响。 - 性能表现和选项2几乎没有差别,唯一的区别就是多了一个几乎不占资源的
INSTANCE实例。
选项4:Class-level val(无const)
这个方案的性能是四种里最差的,具体细节:
- 每个
Thing实例都会持有一个TAG引用字段(虽然JVM字符串常量池会让所有实例的TAG指向同一个字符串对象,但每个实例还是会多占几个字节的引用内存)。 - 访问
TAG必须通过实例的getter方法(比如thing.getTAG()),即使JIT编译器会内联这个getter,本质上还是比直接访问静态字段多了一步操作。 - 因为没有
const修饰,TAG不是编译期常量,编译器无法在调用处直接替换成字面量,每次访问都需要通过实例获取,无法享受编译期优化。
性能总结排序(从优到劣)
- 选项2(顶层const) ≈ 选项3(外部object的const):性能几乎无差别,都是直接访问编译期静态常量,无多余对象开销。
- 选项1(companion object的const):和前两者仅差一个类加载时创建的
Companion实例,对运行时性能无影响,几乎可以视为同一层级。 - 选项4(成员val无const):性能最差,每个实例有额外内存开销,访问速度更慢,无编译期优化。
内容的提问来源于stack exchange,提问作者jpegoraro
相关产品推荐
相关产品推荐

