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泄漏生命周期:
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 内存泄漏与黑屏的关联路径
下图总结了从内存泄漏到黑屏的完整故障传导链:
4.6 排查要点总结
- 关注点:优先检查
dequeueBuffer/queueBuffer配对、new/delete配对、mmap/munmap配对。 - 工具:使用
AddressSanitizer、Valgrind、malloc_debug在HAL进程上开启内存检测。 - 日志:在关键分配/释放点添加
ALOGD日志,记录指针地址和调用栈。 - 压力测试:长时间连续预览/拍照,观察内存占用是否持续增长。