1. 内存泄漏基础:定义、C/C++ 表现与 Camera HAL 性能影响

1.1 内存泄漏的定义

内存泄漏(Memory Leak)是指程序在运行过程中动态分配的堆内存,由于程序设计错误导致在不再需要时未能被释放,使得这部分内存既无法被程序自身再次使用,也无法被操作系统回收。随着程序持续运行,泄漏的内存不断累积,最终导致可用内存枯竭、系统性能下降甚至进程崩溃。

在 C/C++ 这类手动管理内存的语言中,内存泄漏尤为常见。开发者通过 malloc()、calloc()、realloc() 或 new 分配的内存,必须通过 free() 或 delete 显式释放。一旦丢失了指向已分配内存的指针,或释放逻辑被跳过,泄漏便已发生。

核心要点: 内存泄漏的本质是“有分配无释放”或“释放路径不可达”。泄漏的内存对进程而言是“已丢失”的,对系统而言是“已占用”的。

1.2 内存泄漏在 C/C++ 中的典型表现

C/C++ 内存泄漏的表现形式多样,以下列出 Camera HAL 开发中最常见的几种模式:

泄漏模式 代码示例(错误) 说明
直接丢失指针
void leak() {
    int *p = (int*)malloc(100);
    p = NULL; // 原内存地址丢失
}
分配后立即覆盖指针,释放路径永久丢失。
异常路径未释放
void process() {
    char *buf = new char[256];
    if (error) return; // 未 delete[]
    delete[] buf;
}
提前返回或异常抛出时跳过释放代码。
容器中残留指针
std::list<Buffer*> list;
list.push_back(new Buffer());
// 清空 list 前未遍历 delete
容器析构只释放容器自身内存,不释放内部指针指向的对象。
循环引用(C++ 智能指针)
struct A { std::shared_ptr<B> b; };
struct B { std::shared_ptr<A> a; };
// 双方引用计数永不为 0
使用 shared_ptr 时形成闭环,导致对象永远无法析构。
回调/观察者未解注册
listener = new Listener();
service->register(listener);
// 析构时未调用 unregister
外部对象持有本对象指针,本对象析构后外部仍可能调用已释放内存。
常见误区: 很多开发者认为“进程退出时操作系统会回收所有内存,所以泄漏无所谓”。但在 Camera HAL 这类 7×24 小时运行的系统中,泄漏会持续累积,几分钟内即可耗尽系统内存,导致相机服务被 LMK(Low Memory Killer)杀死。

1.3 内存泄漏对 Camera HAL 性能的影响

Camera HAL(Hardware Abstraction Layer)是 Android 相机子系统中直接与硬件驱动交互的底层组件,运行在独立的 cameraserver 进程中。该进程对内存的敏感度极高,内存泄漏会从多个维度破坏其性能:

  • 可用堆内存持续下降: 每次拍照、录像、预览流切换都会分配 buffer、metadata、pipeline 节点等对象。泄漏导致可用内存逐步减少,最终触发 std::bad_alloc 或 ENOMEM 错误。
  • GC / 内存压力加剧: 虽然 HAL 层以 C/C++ 为主,但 Android 系统层会监控 native 进程的内存水位。当内存紧张时,系统频繁触发 kswapd 和 direct reclaim,导致 CPU 开销上升、帧率抖动。
  • Buffer 分配失败: Camera 流依赖 ION/DMA-BUF 分配连续物理内存。native 堆泄漏会碎片化虚拟地址空间,导致大块连续映射失败,预览或录像直接黑屏。
  • 进程被 LMK 杀死: 当 cameraserver 的 oom_score_adj 较高且内存不足时,系统会直接杀死该进程。用户表现为相机应用闪退或“无法连接到相机”。
  • 调试与定位困难: 内存泄漏通常不会立即触发崩溃,而是经过数小时甚至数天后才暴露。在 Camera HAL 这种多线程、多 pipeline 的复杂环境中,定位泄漏源需要结合多种工具。
经验数据: 在主流 Android 设备上,cameraserver 进程的 PSS(Proportional Set Size)正常范围约为 80–150 MB。若连续拍照 100 次后 PSS 增长超过 20 MB,基本可判定存在内存泄漏。

1.4 Camera HAL 内存泄漏的典型链路

下图展示了 Camera HAL 中一次典型的请求-处理-释放流程,以及泄漏可能发生的环节:

Camera HAL 请求处理流程与泄漏风险点 App 发起请求 HAL 分配 Buffer 泄漏点 1: Buffer 未回收到池 ISP 处理 Metadata 填充 泄漏点 2: Metadata 未释放 返回结果 泄漏点 3: 回调未解注册 泄漏累积效应 • 每次拍照泄漏 1 个 Buffer(约 4–16 MB) • 每次拍照泄漏 1 个 Metadata 对象(约 2–8 KB) • 连续拍照 200 次后,PSS 增长可达 800 MB+ • 最终 cameraserver 被 LMK 杀死,相机应用闪退

1.5 泄漏 vs 内存膨胀 vs 内存碎片

在讨论 Camera HAL 内存问题时,需要区分三个容易混淆的概念:

概念 定义 对 Camera HAL 的影响 恢复方式
内存泄漏 已分配内存无法被释放或重用 PSS 持续增长,最终 OOM 必须修复代码
内存膨胀 进程持有过多内存但仍在管理范围内 高分辨率预览时 PSS 升高,停止后回落 调整 buffer 池大小或缓存策略
内存碎片 空闲内存不连续,无法满足大块分配 ION 分配失败,预览黑屏 内存规整或重启进程
诊断提示: 使用 dumpsys meminfo cameraserver 观察 PSS 变化趋势。若连续多次操作后 PSS 不回落,基本可判定为内存泄漏;若 PSS 波动大但最终能回到基线,则更可能是内存膨胀或缓存策略问题。