4. 内存泄漏基础概念:内存泄漏的定义、在C++ HAL层中的常见表现形式

4.1 内存泄漏的定义

内存泄漏(Memory Leak)是指动态分配的堆内存在不再被程序使用后,由于程序逻辑缺陷导致无法被释放或回收,从而永久占用系统内存资源的现象。在C++中,内存泄漏通常表现为使用 new、malloc、mmap 等分配的内存,其指针丢失或未匹配调用 delete、free、munmap。

核心判断标准: 分配(Allocation)与释放(Deallocation)必须成对出现。任何一次分配缺少对应的释放,即构成泄漏。

在Camera HAL层,内存泄漏的后果尤为严重:

  • 黑屏:持续泄漏导致系统可用内存枯竭,Camera HAL无法为新的帧缓冲区(Buffer)分配内存,导致预览或拍照无数据输出。
  • 系统卡顿/重启:Android系统内存压力过大时,触发Low Memory Killer(LMK)杀死关键进程,甚至导致SurfaceFlinger或CameraServer重启。
  • 性能下降:频繁的内存分配与释放导致堆碎片化,分配延迟增加,影响帧率。

4.2 C++ HAL层内存泄漏的常见表现形式

Camera HAL层基于C++开发,大量使用RAII(资源获取即初始化)和智能指针,但以下模式仍极易引入泄漏:

表现形式 典型场景 泄漏根因
裸指针未释放 使用 new 创建 CameraMetadata 或 CaptureRequest,在异常分支中未 delete 缺少 delete 或 free 调用
智能指针循环引用 std::shared_ptr 在 CameraDevice 与 CameraStream 之间互相持有 引用计数无法归零,析构函数不被调用
回调/监听器未解注册 注册 notify() 回调后,在 flush() 或 close() 时未移除 回调对象生命周期延长,内部资源无法释放
Buffer/GraphicBuffer 未归还 HAL 从 BufferQueue 申请 dequeueBuffer 后,未调用 cancelBuffer 或 queueBuffer Buffer 被永久占用,SurfaceFlinger 无法回收
mmap 映射未 unmap 通过 ION 或 DMA-BUF 映射物理内存后,未调用 munmap 虚拟地址空间泄漏,且物理内存无法释放
STL 容器未清空 std::vector<CameraMetadata*> 在 clear() 前未遍历 delete 元素 容器析构仅释放容器自身内存,不释放元素
HAL层特有风险: Camera HAL 运行在独立进程(如 cameraserver)或 vendor 进程中,内存泄漏不会随应用退出而释放。一旦泄漏,必须重启整个 Camera 服务才能恢复,这直接导致黑屏或相机不可用。

4.3 内存泄漏在HAL层中的生命周期模型

下图展示了Camera HAL层中一个典型的Buffer泄漏生命周期:

Camera HAL层 Buffer 泄漏生命周期 App 请求帧 HAL dequeueBuffer HAL 填充数据 queueBuffer 归还 未调用 queue/cancel 正常路径 泄漏路径 Buffer 永久占用 → 黑屏 说明:HAL 从 BufferQueue 取出 Buffer 后,必须通过 queueBuffer 或 cancelBuffer 归还。 任何遗漏都会导致该 Buffer 永久泄漏,最终耗尽可用 Buffer 池,造成黑屏。

4.4 典型泄漏代码示例

以下代码展示了HAL层中一个极易被忽视的裸指针泄漏场景:

// 错误示例:异常分支未释放 metadata
status_t CameraDevice::processCaptureRequest(const CaptureRequest& request) {
    CameraMetadata* metadata = new CameraMetadata(request.metadata);
    
    status_t res = mStream->submitRequest(metadata);
    if (res != OK) {
        // 泄漏点:metadata 未被 delete,直接返回
        return res;
    }
    
    // 正常路径
    delete metadata;
    return OK;
}

// 正确示例:使用智能指针或 RAII 包装
status_t CameraDevice::processCaptureRequestSafe(const CaptureRequest& request) {
    // 使用 unique_ptr 自动管理生命周期
    auto metadata = std::make_unique<CameraMetadata>(request.metadata);
    
    status_t res = mStream->submitRequest(metadata.get());
    if (res != OK) {
        // unique_ptr 析构自动释放,无泄漏
        return res;
    }
    
    // 转移所有权或手动释放
    metadata.release();  // 假设 submitRequest 接管了所有权
    return OK;
}
最佳实践: 在Camera HAL层开发中,应优先使用 std::unique_ptr 和 std::shared_ptr 替代裸指针。对于必须使用裸指针的场景(如与C接口交互),务必使用 RAII 包装类或确保每个 new 都有对应的 delete。

4.5 内存泄漏与黑屏的关联路径

下图总结了从内存泄漏到黑屏的完整故障传导链:

内存泄漏 → 黑屏 故障传导链 内存泄漏 可用内存持续下降 Buffer 分配失败 黑屏 HAL 层特有因素:BufferQueue 耗尽 / ION 内存枯竭 / 回调泄漏 关键结论:内存泄漏是黑屏问题的根本原因之一。在 Camera HAL 层,泄漏不仅消耗堆内存, 还会耗尽 BufferQueue 和 ION 内存等系统级资源,导致 Camera 无法输出任何帧数据。

4.6 排查要点总结

  • 关注点:优先检查 dequeueBuffer/queueBuffer 配对、new/delete 配对、mmap/munmap 配对。
  • 工具:使用 AddressSanitizer、Valgrind、malloc_debug 在HAL进程上开启内存检测。
  • 日志:在关键分配/释放点添加 ALOGD 日志,记录指针地址和调用栈。
  • 压力测试:长时间连续预览/拍照,观察内存占用是否持续增长。