ARCore 中文文档 下载 App

内存预算与 CPU 占用控制

ARCore 应用的内存消耗结构

组件典型内存占用说明
Google Play Services for AR~50~100 MB系统进程,无法控制
ARCore Session~30~50 MBSLAM 地图、Frame 缓冲、Texture 等
摄像头缓冲~20~40 MBRGB + 深度图(如果启用 Depth)
3D 渲染资源因内容而异纹理、Mesh、Shader
Unity/Sceneform 运行时~100~300 MB如果用了游戏引擎
Cloud Anchors 缓冲~10~30 MB托管/解析时临时加载特征点地图

一个典型的 Unity AR 应用,启动后总内存 400~600 MB 是正常的。如果超过 800 MB,低端 Android 设备很容易被系统杀掉。

内存泄漏的常见来源

泄漏一:Anchor 不释放

// 每次 createAnchor 都增加一个 Anchor
// 如果用户每次点击屏幕都创建新 Anchor 而不清理旧的
// 几百次点击后就有几百个 Anchor,每个都持续占用内存

private val anchors = mutableListOf<Anchor>()

fun placeObject(pose: Pose) {
    // 正确:清理旧 Anchor
    anchors.forEach { it.detach() }
    anchors.clear()

    val newAnchor = session.createAnchor(pose)
    anchors.add(newAnchor)
}

泄漏二:Bitmap / 纹理资源不释放

// Augmented Images 动态加载的 Bitmap
val bitmap = BitmapFactory.decodeFile("target_image.png")
database.addImage("name", bitmap)
// bitmap 用完后不能自动释放,需要手动 recycle
bitmap.recycle()

泄漏三:Frame / Image 对象不关闭

// Depth API 的 Image 必须显式关闭
val depthImage = frame.acquireDepthImage()
// 使用 depthImage...
depthImage.close()  // 忘记这行 = 内存泄漏

// 还有 CPU Image(Unity)
cpuImage.Dispose()

泄漏四:Session 重复创建

// 错误:每次进入 AR 页面都 new Session()
fun enterAR() {
    session = Session(context)  // 旧的 Session 没被 destroy!
}

// 正确:复用同一个 Session
fun enterAR() {
    if (session == null) {
        session = Session(context)
    }
    session.resume()
}

// 退出时
fun exitAR() {
    session?.pause()
    // session?.destroy() 某些版本需要 destroy
}

泄漏五:Unity 的 Object.Destroy 时机

// Unity 中手动 Instantiate 的物体需要手动 Destroy
void OnDestroy()
{
    foreach (var anchor in anchors)
    {
        if (anchor != null)
            Destroy(anchor.gameObject);
    }
    anchors.Clear();
}

CPU 峰值控制

ARCore 应用中哪些操作会突然吃掉所有 CPU 核心?

操作影响控制手段
Session 创建首次创建 Session 会做大量初始化提前在后台线程预创建
Cloud Anchors 托管特征点提取和压缩消耗大量 CPU在后台线程做,完成前给 Loading
Session.update()每一帧都在做 SLAM、平面检测、光照估算不要在 update 之后做额外重计算
Augmented Image 数据库编译addImage() 会同步做特征点提取大量图片预先离线编译(arcoreimg 工具)
Depth API 深度图生成被动深度模式下 CPU 计算量很大低端设备自动关闭或只用硬件深度
Unity 的 LateUpdate把重计算放在 LateUpdate 里会抢时间片用协程或 Job System 做异步处理

Unity Job System 优化

把耗时计算移到后台线程:

// 用 C# Job System 做深度图处理
[BurstCompile]
public struct DepthProcessingJob : IJob
{
    public NativeArray<float> depthData;
    public NativeArray<float> result;

    public void Execute()
    {
        // 在后台线程处理深度图
        // 不阻塞主线程的帧渲染
    }
}

void Update()
{
    var job = new DepthProcessingJob { ... };
    job.Schedule().Complete();  // 或用 IJobHandle 异步等待
}

后台处理:Activity 暂停后的清理

当用户切换到其他应用时(Activity.onPause),必须做以下清理:

override fun onPause() {
    super.onPause()

    // 1. 暂停 Session(停止 SLAM 计算)
    arSession?.pause()

    // 2. 停止 GL 渲染(释放 GPU 资源)
    glSurfaceView?.onPause()

    // 3. 移除所有运行时任务
    mainHandler.removeCallbacksAndMessages(null)

    // 4. 释放可释放的资源(Bitmap、非持久化 Anchor 等)
    tempAnchors.forEach { it.detach() }
    tempBitmaps.forEach { it.recycle() }

    // 5. 保留持久性资源(Session 本身不需要 destroy,pause 足够)
}

override fun onResume() {
    super.onResume()

    // 恢复 Session 和渲染
    glSurfaceView?.onResume()
    arSession?.resume()
}

OOM(内存溢出)崩溃预防

预防措施说明
监测内存使用定期检查 Debug.getMemoryInfo(),超过阈值时主动降级
纹理压缩用 ASTC 格式(Android 通用压缩格式),比 PNG/JPG 省 50~70%
按需加载不要启动时加载所有 3D 模型和纹理,只加载当前场景需要的
控制纹理尺寸UI 纹理 ≤ 1024×1024,3D 纹理 ≤ 2048×2048
Unity Build Settings关闭 "Development Build"(Debug Build 会占很多额外内存)
Texture StreamingUnity 的 Texture Streaming 功能根据相机距离动态加载/卸载纹理

iOS 上的内存管理

iOS 对后台运行的应用更严格:

监测工具

工具平台能力
Android Studio Memory ProfilerAndroid实时内存分配、泄漏检测、Heap Dump 分析
Unity Memory ProfilerUnity更详细的内存快照比较
adb shell dumpsys meminfoAndroid命令行查看进程内存
Xcode Memory GraphiOS对象引用图、泄漏检测
LeakCanaryAndroid自动检测 Activity/Fragment 泄漏