Skip to content

对象冻结机制已废弃 - Kotlin 1.9.20 完全移除

源:Kotlin/Native Memory Manager

对象冻结是传统内存模型的核心机制,用于实现线程安全的对象共享。虽然已被新内存模型取代,但理解其设计有助于维护遗留代码和理解 Kotlin/Native 的演进历程。

冻结机制设计

为什么需要冻结

传统 Kotlin/Native 采用严格的线程隔离模型

每个线程拥有独立的对象堆

可变对象只能被单个线程访问

跨线程共享必须保证不可变性

通过 freeze() 实现对象不可变

这种设计避免了数据竞争,但增加了编程复杂度[来源:Touchlab KN Concurrency Guide]。

freeze() 方法

kotlin
@OptIn(FreezingIsDeprecated::class)
import kotlin.native.concurrent.*

class MutableState(var value: Int)

fun basicFreeze() {
    val data = MutableState(42)
    
    // 冻结前:完全可变
    data.value = 100       // ✅ OK
    println(data.isFrozen) // false
    
    // 执行冻结
    data.freeze()
    
    // 冻结后:完全不可变
    println(data.isFrozen) // true
    println(data.value)    // ✅ 100 (只能读取)
    
    // ❌ 任何修改都会抛出 InvalidMutabilityException
    try {
        data.value = 200
    } catch (e: InvalidMutabilityException) {
        println("Cannot modify frozen object: ${e.message}")
    }
}

核心特性

  • 不可逆:一旦冻结,永远无法解冻
  • 递归:冻结会传播到整个对象图
  • 线程安全:冻结对象可在所有线程间安全共享

冻结传播

深度冻结

冻结操作会递归遍历整个对象图,冻结所有可达对象:

kotlin
@OptIn(FreezingIsDeprecated::class)
class Node(val value: Int, var next: Node? = null)

fun freezePropagation() {
    // 创建链表 1 → 2 → 3
    val node1 = Node(1)
    val node2 = Node(2)
    val node3 = Node(3)
    
    node1.next = node2
    node2.next = node3
    
    // 仅冻结 node1
    node1.freeze()
    
    // 验证传播:整个链表都被冻结
    println("node1.isFrozen: ${node1.isFrozen}") // true
    println("node2.isFrozen: ${node2.isFrozen}") // true ← 自动传播
    println("node3.isFrozen: ${node3.isFrozen}") // true ← 自动传播
    
    // ❌ 无法修改链表中的任何节点
    // node2.next = null  // InvalidMutabilityException
    // node3.value = 99   // val 本身不可变,但如果是 var 会抛异常
}

复杂对象图

kotlin
@OptIn(FreezingIsDeprecated::class)
data class Person(
    val name: String,
    var age: Int,
    val friends: MutableList<Person> = mutableListOf()
)

fun complexGraphFreeze() {
    val alice = Person("Alice", 30)
    val bob = Person("Bob", 25)
    val charlie = Person("Charlie", 35)
    
    // 构建好友关系
    alice.friends.add(bob)
    alice.friends.add(charlie)
    bob.friends.add(alice)
    
    // 冻结 Alice
    alice.freeze()
    
    // 验证:所有人和列表都被冻结
    println("alice.isFrozen: ${alice.isFrozen}")           // true
    println("bob.isFrozen: ${bob.isFrozen}")               // true ← 通过 friends 传播
    println("charlie.isFrozen: ${charlie.isFrozen}")       // true ← 通过 friends 传播
    println("alice.friends.isFrozen: ${alice.friends.isFrozen}") // true
    
    // ❌ 所有修改操作都失败
    // alice.age = 31                   // InvalidMutabilityException
    // bob.age = 26                     // InvalidMutabilityException
    // alice.friends.add(Person("Dave", 28)) // InvalidMutabilityException
}

Worker 线程模型集成

TransferMode.SAFE

Worker API 与冻结机制紧密集成:

kotlin
@OptIn(ObsoleteWorkersApi::class, FreezingIsDeprecated::class)
import kotlin.native.concurrent.Worker

fun workerAutoFreeze() {
    val worker = Worker.start()
    val data = MutableList(5) { it * 10 }
    
    println("Before transfer: ${data.isFrozen}") // false
    
    // SAFE 模式:自动冻结传递的对象
    val future = worker.execute(TransferMode.SAFE, { data }) { input ->
        println("In worker: ${input.isFrozen}") // true ← 自动冻结
        
        // ❌ 无法修改
        // input[0] = 999  // InvalidMutabilityException
        
        input.sum() // 只能读取
    }
    
    val result = future.result
    println("Result: $result") // 100
    
    // 原对象也被冻结了
    println("After transfer: ${data.isFrozen}") // true
    
    worker.requestTermination().result
}

TransferMode.UNSAFE

kotlin
@OptIn(ObsoleteWorkersApi::class, FreezingIsDeprecated::class)
fun workerUnsafeMode() {
    val worker = Worker.start()
    val data = mutableListOf(1, 2, 3)
    
    // 手动冻结
    data.freeze()
    
    // UNSAFE 模式:跳过检查(危险!)
    worker.execute(TransferMode.UNSAFE, { data }) { input ->
        println("Received: $input")
        input.sum()
    }.result
    
    worker.requestTermination().result
}

检查与验证

isFrozen 属性

所有对象都有 isFrozen 属性:

kotlin
@OptIn(FreezingIsDeprecated::class)
fun checkFrozenStatus() {
    val list = mutableListOf(1, 2, 3)
    val map = mutableMapOf("key" to "value")
    
    // 动态检查
    if (!list.isFrozen) {
        list.add(4)  // 安全:未冻结可以修改
    }
    
    list.freeze()
    
    if (list.isFrozen) {
        // 只能读取
        println("List size: ${list.size}")
        val copy = list.toList() // 创建可变副本
    }
}

ensureNeverFrozen()

标记对象永远不应被冻结:

kotlin
@OptIn(FreezingIsDeprecated::class)
class MustRemainMutable {
    var counter = 0
    
    init {
        // 确保此对象永远不会被冻结
        ensureNeverFrozen()
    }
    
    fun increment() {
        counter++
    }
}

fun tryFreezeProtected() {
    val obj = MustRemainMutable()
    obj.increment() // ✅ OK
    
    try {
        obj.freeze()
        println("Freeze succeeded (unexpected)")
    } catch (e: IncorrectDereferenceException) {
        println("❌ Freeze failed as expected: ${e.message}")
        // 错误:对象被 ensureNeverFrozen() 保护
    }
}

常见问题与解决方案

问题1:意外冻结大量对象

kotlin
@OptIn(FreezingIsDeprecated::class)
// ❌ 问题代码
class AppConfig {
    val database = DatabaseConnection()
    val cache = mutableMapOf<String, Any>()
    var debugMode = false
}

fun problemCode() {
    val config = AppConfig()
    
    // 传递给 Worker 时会冻结整个 config
    // 包括 database 和 cache
    config.freeze()
    
    // ❌ 后续无法修改 cache 或 debugMode
}

解决方案:使用 @SharedImmutable 或分离可变部分

kotlin
@OptIn(FreezingIsDeprecated::class)
// ✅ 改进:分离可变和不可变部分
@SharedImmutable
val sharedConfig = object {
    val databaseUrl = "jdbc:..."
    val apiEndpoint = "https://api.example.com"
}

// 可变部分保持在线程本地
class MutableAppState {
    var debugMode = false
    val cache = mutableMapOf<String, Any>()
}

问题2:InvalidMutabilityException 调试

kotlin
@OptIn(FreezingIsDeprecated::class)
fun debugFreeze() {
    val data = mutableListOf("a", "b", "c")
    
    // 在关键位置检查
    println("Before operation: isFrozen=${data.isFrozen}")
    
   someOperation(data)
    
    println("After operation: isFrozen=${data.isFrozen}")
    
    if (data.isFrozen) {
        println("⚠️ Data was frozen by someOperation")
        // 追踪哪里冻结的
    }
}

迁移到新内存模型

移除 freeze 调用

kotlin
@OptIn(FreezingIsDeprecated::class, ObsoleteWorkersApi::class)
class DataProcessor {
    fun processInBackground(data: List<Int>) {
        val worker = Worker.start()
        
        // 旧模型:必须冻结
        data.freeze()
        
        worker.execute(TransferMode.SAFE, { data }) {
            it.sum()
        }
        
        worker.requestTermination()
    }
}
kotlin
import kotlinx.coroutines.*

// ✅ 新模型:无需冻结
class DataProcessor {
    suspend fun processInBackground(data: List<Int>) = withContext(Dispatchers.Default) {
        data.sum() // 直接使用,无需冻结
    }
}

替换 @SharedImmutable

kotlin
// Legacy
@OptIn(FreezingIsDeprecated::class)
@SharedImmutable
val globalConstants = mapOf(
    "API_KEY" to "...",
    "VERSION" to "1.0"
)

// ✅ New MM:直接定义全局变量
val globalConstants = mapOf(
    "API_KEY" to "...",
    "VERSION" to "1.0"
)
// 新模型:全局变量自动可跨线程访问,无需标注

移除 ensureNeverFrozen

kotlin
// Legacy
@OptIn(FreezingIsDeprecated::class)
class MutableCache {
    init {
        ensureNeverFrozen() // ❌ 删除
    }
    
    var data = mutableMapOf<String, String>()
}

// ✅ New MM:添加必要的同步
class MutableCache {
    private val data = mutableMapOf<String, String>()
    private val lock = Any()
    
    fun get(key: String): String? = synchronized(lock) {
        data[key]
    }
    
    fun put(key: String, value: String) = synchronized(lock) {
        data[key] = value
    }
}

性能影响

冻结开销

kotlin
@OptIn(FreezingIsDeprecated::class, ExperimentalTime::class)
import kotlin.time.measureTime

fun freezePerformance() {
    // 大对象图
    val root = Node(0)
    var current = root
    repeat(10_000) {
        val next = Node(it + 1)
        current.next = next
        current = next
    }
    
    // 测量冻结时间
    val duration = measureTime {
        root.freeze()
    }
    
    println("Freezing 10K nodes took: ${duration.inWholeMilliseconds}ms")
    // 典型结果:5-15ms(取决于硬件)
}

对象冻结机制在新内存模型中已完全移除。迁移时只需删除 freeze() 调用和相关注解,并根据需要添加适当的同步机制(如 synchronizedAtomicInt 等)。新模型大幅简化了并发编程,性能也有显著提升。