Kotlin 文档包含以下信息。
对 a 的所有可空引用实际上都是同一个对象,因为 JVM 对 -128 到 127 之间的整数应用了内存优化。它不适用于 b 引用,因此它们是不同的对象。我编写了主要函数和测试代码来检查上述内容。 这时候,每个结果都不同了。
我使用 kotest 对此进行了测试。
class Ch7Test: StringSpec({
"nullable Int between -128 and 127 should be caching" {
val boxedA: Int? = 100
val anotherBoxedA: Int? = 100
assertSoftly {
(boxedA === anotherBoxedA) shouldBe true
}
}
})
测试结果失败。
expected:<true> but was:<false>
Expected :true
Actual :false
但是当我在main函数中测试时,结果与测试结果不同。
fun main() {
val boxedA: Int? = 100
val anotherBoxedA: Int? = 100
println("debug: ${boxedA === anotherBoxedA}") // output was "debug: true"
}
为什么逻辑一样,结果却不一样?
"foo bar bar" { ... }
中执行
StringSpec
时,您正在调用
String.invoke
中声明的 StringSpecRootScope
。
operator fun String.invoke(test: suspend StringSpecScope.() -> Unit)
这里重要的部分是,您传递的 lambda 实际上是一个 suspend
lambda。换句话说,装箱和测试发生在协程中。显然,协程中的拳击是不同的。不是调用像
Integer.valueOf
这样的 Java 装箱方法,而是调用this file 中的方法之一。对于
Int
s,它是:
internal fun boxInt(primitive: Int): java.lang.Integer = java.lang.Integer(primitive)
如您所见,这直接创建了一个新的 Integer
对象(在 Java 中为
new Integer(primitive)
)。如果它使用了
Integer.valueOf
,那么就会使用整数缓存。根据添加此文件的
commit,
这些方法是原始包装类的非常薄的包装 构造函数。所以这实际上是专门针对HotSpot JVM的优化。 JVM 无法在每种情况下消除分配(否则您的测试不会失败),但总的来说,这为协程代码提供了净收益。它们由返回原语的协程代码使用,这样 HotSpot 能够完全丢弃分配。
#KT-26591已修复
也就是说,我不熟悉 JVM 内部结构,无法准确解释显式分配
Integer
实际上如何使 JVM优化 分配。