分析和优化游戏内存
Profile and optimize your game's memory
2022年6月6日
一句话判断
“内存分配不等于实际内存占用”——如果你的游戏因为内存问题被系统终止,这场 Session 会帮你搞清楚到底发生了什么以及如何诊断。
这场 Session 讲了什么
Apple GPU 软件团队的 Jack Xu 和 Seth Lu 系统讲解了游戏内存的分析和优化方法。内容从内存的基本概念开始,逐步深入到 Instruments 的内存分析工具、Xcode 的内存图分析、以及 Metal Debugger 的资源优化。
Session 的核心洞察是:分配(Allocations)和实际内存使用(Memory Footprint)是两个不同的概念。分配发生在虚拟地址空间,实际内存使用发生在物理内存。系统按物理内存页面(16KB)向你的游戏收费,不是按分配量收费。
今年新增了 Game Memory Instruments 模板,专门用于分析 Metal 游戏的内存增长。
值得深挖的点
分配 vs 实际内存。 游戏申请内存分配时,空间预留在了虚拟地址空间。只有当游戏实际访问这些内存时,系统才会在物理内存中准备空间。这意味着分配 100MB 不一定占用 100MB 物理内存。内存页分为三类:Dirty(已写入的内存)、Compressed(被压缩的不活跃页面)、Clean(只读映射文件,不计入 footprint)。
Memory Footprint 是核心指标。 Footprint = Dirty + Compressed + Swapped。这是系统用于执行内存限制的指标。在 Apple Silicon 上,CPU 和 GPU 共享统一内存,Metal 资源也计入 Dirty 内存。你的游戏可以通过 API 查询当前 footprint 和可用内存。
Game Memory 模板。 Instruments 今年新增的模板包含多个工具:Allocations 记录分配历史、Metal Resource Events 追踪 Metal 资源、VM Tracker 记录内存 footprint、Virtual Memory Trace 追踪虚拟内存活动。这个模板专为游戏内存分析设计。
内存页面的生命周期。 每个内存页面 16KB。不活跃的 Dirty 页面可能被系统压缩或换出到磁盘。当游戏再次访问这些页面时,系统会解压或从磁盘换入。你的游戏仍然按未压缩大小被收费。
代码片段
import os.proc
// 查询当前可用的系统内存(iOS/iPadOS/tvOS)
let availableMemory = os_proc_available_memory()
print("可用内存: \(availableMemory / 1024 / 1024) MB")
// 查询当前游戏的内存 footprint
import os.proc
var info = rusage_info_v6()
var infoCount = mach_msg_type_number_t(MemoryLayout<rusage_info_v6>.size / MemoryLayout<integer_t>.size)
let result = withUnsafeMutablePointer(to: &info) { infoPtr in
infoPtr.withMemoryRebound(to: &infoCount, capacity: 1) { countPtr in
proc_pid_rusage(getpid(), RUSAGE_INFO_V6, countPtr)
}
}
if result == 0 {
let footprint = info.ri_phys_footprint
let lifetimeMax = info.ri_lifetime_max_phys_footprint
print("当前 footprint: \(footprint / 1024 / 1024) MB")
print("生命周期最大 footprint: \(lifetimeMax / 1024 / 1024) MB")
}
最佳实践
- 关注 Memory Footprint 而非分配量——前者是系统限制和终止的依据
- 使用 Xcode 的 Memory Report 作为第一步概览,用 Instruments 的 Game Memory 模板做深入分析
- 从新启动开始分析(而非附加到已有进程),确保捕获完整的启动期内存增长
- Apple Silicon 上 Metal 资源计入 Dirty 内存,优化纹理和缓冲区大小能直接减少 footprint
- 使用
os_proc_available_memory()在运行时动态调整资源使用策略 - Clean 内存虽然不计入 footprint,但过多的文件映射会拖慢系统
还有什么值得关注
- Metal Debugger 可以分析 Metal 资源(纹理、缓冲区、管线状态对象)的使用效率
- Xcode 的内存图调试器可以分析对象引用关系,发现内存泄漏
- 不同 Apple 平台有不同的内存限制,需要针对目标设备进行测试
- 压缩内存仍然被收费,只是占用的物理内存更少——但它会增加 CPU 开销