Skip to content

手动内存管理

源:Manual Memory Management

在某些场景下,需要绕过 GC 直接管理内存。Kotlin/Native 提供了 nativeHeap 用于手动内存分配和释放。

nativeHeap API

基本分配

kotlin
@OptIn(ExperimentalForeignApi::class)
import kotlinx.cinterop.*

// 分配单个对象
val intVar: IntVar = nativeHeap.alloc<IntVar>()
intVar.value = 42

// 使用完毕后必须释放
nativeHeap.free(intVar)

// 分配数组
val buffer: CPointer<ByteVar> = nativeHeap.allocArray<ByteVar>(1024)
// ... 使用 buffer ...
nativeHeap.free(buffer)

RAII 包装器

kotlin
@OptIn(ExperimentalForeignApi::class)
class NativeMemory<T : CVariable>(
    private val placement: NativePlacement = nativeHeap
) : AutoCloseable {
    private val allocated = mutableListOf<CPointer<*>>()
    
    inline fun <reified R : T> alloc(): R {
        val ptr = placement.alloc<R>()
        allocated.add(ptr.ptr)
        return ptr
    }
    
    fun allocArray(size: Int): CPointer<T> {
        val ptr = placement.allocArray<T>(size)
        allocated.add(ptr)
        return ptr
    }
    
    override fun close() {
        allocated.forEach { placement.free(it) }
        allocated.clear()
    }
}

// 使用
NativeMemory<IntVar>().use { mem ->
    val data = mem.alloc<IntVar>()
    data.value = 100
}  // 自动释放

Arena 分配器

自定义 Arena

kotlin
@OptIn(ExperimentalForeignApi::class)
class Arena : NativePlacement {
    private val allocations = mutableListOf<CPointer<*>>()
    
    override fun alloc(size: Long, align: Int): NativePointed {
        val ptr = nativeHeap.alloc(size, align)
        allocations.add(ptr.rawPtr)
        return ptr
    }
    
    override fun alloc(size: Int, align: Int): NativePointed {
        return alloc(size.toLong(), align)
    }
    
    fun clear() {
        allocations.forEach { nativeHeap.free(it) }
        allocations.clear()
    }
}

// 使用
val arena = Arena()
val ptr1 = arena.alloc<IntVar>()
val ptr2 = arena.allocArray<ByteVar>(100)
// ... 使用 ...
arena.clear()  // 一次性释放所有

内存池

固定大小对象池

kotlin
@OptIn(ExperimentalForeignApi::class)
class FixedSizePool<T : CVariable>(
    private val blockSize: Int,
    initialCapacity: Int = 16
) {
    private val freeList = ArrayDeque<CPointer<T>>()
    
    init {
        repeat(initialCapacity) {
            freeList.add(nativeHeap.allocArray(blockSize))
        }
    }
    
    fun acquire(): CPointer<T> {
        return synchronized(freeList) {
            if (freeList.isNotEmpty()) {
                freeList.removeFirst()
            } else {
                nativeHeap.allocArray(blockSize)
            }
        }
    }
    
    fun release(ptr: CPointer<T>) {
        synchronized(freeList) {
            // 清零内存
            platform.posix.memset(ptr, 0, blockSize.toULong())
            freeList.add(ptr)
        }
    }
    
    fun destroy() {
        freeList.forEach { nativeHeap.free(it) }
        freeList.clear()
    }
}

// 使用
val bufferPool = FixedSizePool<ByteVar>(4096, initialCapacity = 10)
val buffer = bufferPool.acquire()
// ... 使用 buffer ...
bufferPool.release(buffer)

性能优化

批量分配

kotlin
@OptIn(ExperimentalForeignApi::class)
class BatchAllocator(private val batchSize: Int = 1024) {
    private var currentBatch: CPointer<ByteVar>? = null
    private var offset = 0
    
    fun allocBytes(size: Int): CPointer<ByteVar> {
        if (currentBatch == null || offset + size > batchSize) {
            currentBatch = nativeHeap.allocArray(batchSize)
            offset = 0
        }
        
        val result = currentBatch!! + offset
        offset += size
        return result
    }
    
    // 注意:批量分配无法单独释放,只能整批释放
    fun reset() {
        currentBatch?.let { nativeHeap.free(it) }
        currentBatch = null
        offset = 0
    }
}

对齐分配

kotlin
@OptIn(ExperimentalForeignApi::class)
fun allocAligned(size: Int, alignment: Int): CPointer<ByteVar> {
    val rawPtr = nativeHeap.allocArray<ByteVar>(size + alignment)
    val addr = rawPtr.rawValue.toLong()
    val alignedAddr = (addr + alignment - 1) and (alignment - 1).inv().toLong()
    return alignedAddr.toCPointer()!!
}

// 使用 SIMD 需要16字节对齐
val simdBuffer = allocAligned(1024, 16)

内存泄漏检测

追踪分配

kotlin
@OptIn(ExperimentalForeignApi::class)
object MemoryTracker {
    private val allocations = mutableMapOf<Long, AllocationInfo>()
    
    data class AllocationInfo(
        val size: Int,
        val stackTrace: String
    )
    
    fun track(ptr: CPointer<*>, size: Int) {
        synchronized(allocations) {
            allocations[ptr.rawValue.toLong()] = AllocationInfo(
                size = size,
                stackTrace = Exception().stackTraceToString()
            )
        }
    }
    
    fun untrack(ptr: CPointer<*>) {
        synchronized(allocations) {
            allocations.remove(ptr.rawValue.toLong())
        }
    }
    
    fun reportLeaks() {
        synchronized(allocations) {
            if (allocations.isNotEmpty()) {
                println("=== Memory Leaks Detected ===")
                allocations.forEach { (addr, info) ->
                    println("Address: 0x${addr.toString(16)}")
                    println("Size: ${info.size} bytes")
                    println("Allocated at:\n${info.stackTrace}")
                    println("---")
                }
            }
        }
    }
}

// 包装 nativeHeap
@OptIn(ExperimentalForeignApi::class)
object TrackedHeap {
    inline fun <reified T : CVariable> alloc(): T {
        val ptr = nativeHeap.alloc<T>()
        MemoryTracker.track(ptr.ptr, sizeOf<T>().toInt())
        return ptr
    }
    
    fun <T : CVariable> free(ptr: T) {
        MemoryTracker.untrack(ptr.ptr)
        nativeHeap.free(ptr)
    }
}

与 C 内存互操作

接收 C 分配的内存

kotlin
@OptIn(ExperimentalForeignApi::class)
import platform.posix.malloc
import platform.posix.free

// C: void* get_buffer(size_t size)
fun receiveCMemory(size: Int): CPointer<ByteVar>? {
    val cPtr = malloc(size.toULong())?.reinterpret<ByteVar>()
    
    // 使用完毕后用 C 的 free 释放
    // free(cPtr)
    
    return cPtr
}

传递给 C 代码

kotlin
@OptIn(ExperimentalForeignApi::class)
fun passToCCode() {
    // Kotlin 分配
    val buffer = nativeHeap.allocArray<ByteVar>(1024)
    
    // 传递给 C 函数
    // C: void process_buffer(uint8_t* data, size_t size)
    process_buffer(buffer, 1024u)
    
    // Kotlin 负责释放
    nativeHeap.free(buffer)
}

手动内存管理提供了最大的控制权,但需要严格的纪律避免泄漏。合理使用 RAII 模式和内存池能够在保证安全的同时获得最佳性能。