You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Kotlin/Native中将CVariable复制到堆内存?

将memScoped中的CVariable复制到堆内存以避免Use-After-Free

问题根源

你代码中在memScoped块内分配的VkExtent2D存储在栈内存中,离开memScoped作用域后,该内存会被自动释放。后续使用返回的VkExtent2D实例时,访问的是已释放的内存,因此出现随机值,这是典型的Use-After-Free未定义行为。

解决方案:使用nativeHeap分配堆内存

在Kotlin/Native中,要让C类型变量脱离memScoped的生命周期限制,需要使用nativeHeap在原生堆上分配内存,再将栈上计算好的值复制过去。这种方式的内存不会自动释放,需要手动管理(或通过包装类自动释放)。

方法1:手动管理堆内存

修改chooseSwapExtent函数,直接在堆上分配VkExtent2D,并在memScoped内完成值的初始化:

private fun chooseSwapExtent(capabilities: VkSurfaceCapabilitiesKHR): VkExtent2D {
    if (capabilities.currentExtent.width != UInt.MAX_VALUE) {
        // 直接在堆上分配并复制已有值
        val heapExtent = nativeHeap.alloc<VkExtent2D>()
        heapExtent.width = capabilities.currentExtent.width
        heapExtent.height = capabilities.currentExtent.height
        return heapExtent
    } else {
        val heapExtent = nativeHeap.alloc<VkExtent2D>()
        memScoped {
            val width = alloc<IntVar>()
            val height = alloc<IntVar>()
            glfwGetFramebufferSize(
                vulkan.klava.glfw.window.window,
                width.ptr, height.ptr
            )
            println("GLFW framebuffer size: ${width.value}x${height.value}")

            println("GPU minImageExtent: ${capabilities.minImageExtent.width}x${capabilities.minImageExtent.height}")
            println("GPU maxImageExtent: ${capabilities.maxImageExtent.width}x${capabilities.maxImageExtent.height}")

            // 将计算好的值赋值给堆上的实例
            heapExtent.width = width.value.toUInt().coerceIn(
                capabilities.minImageExtent.width,
                capabilities.maxImageExtent.width
            )
            heapExtent.height = height.value.toUInt().coerceIn(
                capabilities.minImageExtent.height,
                capabilities.maxImageExtent.height
            )
        }
        return heapExtent
    }
}

使用时,务必在不再需要该实例时手动释放内存,避免泄漏:

val extent = chooseSwapExtent(swapChainSupport.capabilities)
// 使用extent完成操作
// ...
// 释放堆内存
nativeHeap.free(extent)

方法2:用AutoCloseable自动管理内存

为了避免忘记手动释放内存,可以将堆分配的实例包装成AutoCloseable实现类,通过use块自动释放:

// 包装类,负责内存的自动释放
class HeapVkExtent2D(private val extent: VkExtent2D) : AutoCloseable {
    val value: VkExtent2D get() = extent

    override fun close() {
        nativeHeap.free(extent)
    }
}

private fun chooseSwapExtent(capabilities: VkSurfaceCapabilitiesKHR): HeapVkExtent2D {
    if (capabilities.currentExtent.width != UInt.MAX_VALUE) {
        val heapExtent = nativeHeap.alloc<VkExtent2D>()
        heapExtent.width = capabilities.currentExtent.width
        heapExtent.height = capabilities.currentExtent.height
        return HeapVkExtent2D(heapExtent)
    } else {
        val heapExtent = nativeHeap.alloc<VkExtent2D>()
        memScoped {
            val width = alloc<IntVar>()
            val height = alloc<IntVar>()
            glfwGetFramebufferSize(
                vulkan.klava.glfw.window.window,
                width.ptr, height.ptr
            )
            println("GLFW framebuffer size: ${width.value}x${height.value}")

            println("GPU minImageExtent: ${capabilities.minImageExtent.width}x${capabilities.minImageExtent.height}")
            println("GPU maxImageExtent: ${capabilities.maxImageExtent.width}x${capabilities.maxImageExtent.height}")

            heapExtent.width = width.value.toUInt().coerceIn(
                capabilities.minImageExtent.width,
                capabilities.maxImageExtent.width
            )
            heapExtent.height = height.value.toUInt().coerceIn(
                capabilities.minImageExtent.height,
                capabilities.maxImageExtent.height
            )
        }
        return HeapVkExtent2D(heapExtent)
    }
}

使用时通过use块自动释放内存,更安全:

chooseSwapExtent(swapChainSupport.capabilities).use { heapExtent ->
    val extent = heapExtent.value
    // 在这里使用extent进行操作
}
// 离开use块后,内存会自动释放

核心原理

memScoped仅管理栈上的临时内存,作用域结束后自动回收;而nativeHeap.alloc<T>()在原生堆上分配内存,生命周期由开发者控制,适合需要长期使用的C类型变量。

内容的提问来源于stack exchange,提问作者xdevs23

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.26 06:43:24