3. Buffer管理基础:Gralloc Buffer分配、生命周期与引用计数机制

在Camera HAL Pipeline中,Buffer是承载图像数据的核心载体。Gralloc作为Android图形内存分配器,负责管理Buffer的分配、映射、共享与回收。理解Gralloc Buffer的生命周期与引用计数机制,是优化数据传输、避免内存泄漏与帧率抖动的关键前提。

3.1 Gralloc Buffer分配流程

Gralloc Buffer的分配涉及HAL层与内核驱动层的协作。分配请求通常由Camera HAL的Stream消费者(如Surface)发起,经过Gralloc HAL模块完成物理内存分配与CPU/GPU映射。

关键分配参数: 宽度、高度、像素格式(如NV12、YUV420)、使用标志(USAGE_HW_CAMERA_READ、USAGE_HW_TEXTURE等)、层数(用于multi-plane)。

3.1.1 分配接口与流程

Gralloc分配的核心接口为 alloc() 或 allocate(),其典型调用链如下:

  1. BufferQueue生产者请求:Camera HAL通过 dequeueBuffer() 向BufferQueue申请空闲Buffer。
  2. Gralloc分配触发:若BufferQueue中无可用Buffer,则调用Gralloc的 alloc() 分配新Buffer。
  3. 内存分配:Gralloc HAL根据使用标志选择分配器(ION、DMA-BUF、System Heap),分配连续或非连续物理内存。
  4. 映射与句柄生成:分配完成后,返回 buffer_handle_t 句柄,并完成CPU/GPU地址映射。
  5. Buffer入队:新分配的Buffer加入BufferQueue的空闲队列,等待Camera HAL填充数据。
性能提示: 在Camera HAL中,建议在Stream配置阶段预分配足够数量的Buffer(通常为3~8个),避免在运行过程中频繁触发分配,减少帧率抖动。

3.1.2 分配参数对比表

参数 说明 典型值 性能影响
width / height 图像分辨率 1920x1080 直接影响内存大小与分配耗时
format 像素格式 HAL_PIXEL_FORMAT_YCbCr_420_SP 决定内存布局与对齐要求
usage 使用标志位组合 GRALLOC_USAGE_HW_CAMERA_WRITE 影响内存类型与映射方式
layerCount 平面数 1(单平面)或 2(双平面) 多平面增加映射复杂度

3.2 Buffer生命周期管理

Gralloc Buffer的生命周期从分配开始,到释放结束,中间经历多个状态转换。理解这些状态是正确管理Buffer引用计数的前提。

3.2.1 生命周期状态机

以下SVG图展示了Gralloc Buffer在Camera HAL Pipeline中的典型生命周期状态转换:

Gralloc Buffer生命周期状态机 FREE(空闲) DEQUEUED(出队) QUEUED(入队) ACQUIRED(获取) RELEASED(释放) ALLOCATED(已分配) dequeueBuffer queueBuffer acquireBuffer releaseBuffer cancelBuffer freeBuffer 说明:虚线箭头表示Buffer释放回内存池;实线箭头表示Buffer在生产者与消费者之间的流转。 每个状态转换都伴随着引用计数的增减操作。

3.2.2 生命周期关键阶段说明

阶段 描述 引用计数变化
FREE Buffer在BufferQueue空闲队列中等待使用 refcount = 0
DEQUEUED 生产者(Camera HAL)获取Buffer用于填充数据 refcount = 1(生产者持有)
QUEUED 生产者填充完成,Buffer入队等待消费者处理 refcount = 1(BufferQueue持有)
ACQUIRED 消费者(如Display、Encoder)获取Buffer进行读取 refcount = 1(消费者持有)
RELEASED 消费者处理完成,Buffer释放回BufferQueue refcount = 0
ALLOCATED Buffer已分配内存但尚未加入BufferQueue refcount = 0(仅分配)

3.3 引用计数机制

Gralloc Buffer的引用计数(refcount)是确保Buffer在多个消费者之间安全共享的核心机制。每个Buffer内部维护一个原子计数器,记录当前持有该Buffer的实体数量。

3.3.1 引用计数增减规则

  • 增加引用:当生产者调用 dequeueBuffer() 或消费者调用 acquireBuffer() 时,Buffer的引用计数加1。
  • 减少引用:当生产者调用 cancelBuffer() 或消费者调用 releaseBuffer() 时,引用计数减1。
  • 零引用释放:当引用计数降为0时,Buffer被回收到空闲池,或根据策略释放物理内存。
常见陷阱: 在Camera HAL中,如果生产者未正确调用 cancelBuffer() 或 queueBuffer(),会导致Buffer引用计数泄漏,最终耗尽Buffer池,造成预览或录像卡顿。务必在异常路径中确保Buffer被正确归还。

3.3.2 引用计数与Buffer共享

在Camera HAL Pipeline中,同一个Buffer可能被多个消费者共享(如预览与编码同时使用)。此时引用计数机制确保Buffer在所有消费者都释放后才被回收。

以下SVG图展示了多消费者场景下的引用计数变化:

多消费者共享Buffer引用计数示例 Camera HAL(生产者) Gralloc Buffer refcount = 3 Display(消费者1) Video Encoder(消费者2) Snapshot(消费者3) queueBuffer acquireBuffer acquireBuffer acquireBuffer 说明:当三个消费者都调用 acquireBuffer() 后,Buffer的引用计数变为3。 每个消费者处理完毕后调用 releaseBuffer(),引用计数依次递减。 只有当所有消费者都释放后(refcount=0),Buffer才被回收。

3.4 引用计数与内存泄漏防范

在Camera HAL开发中,引用计数管理不当是导致内存泄漏的主要原因之一。以下是一些最佳实践:

  • 配对调用:确保每个 dequeueBuffer() 都有对应的 queueBuffer() 或 cancelBuffer()。
  • 异常处理:在错误路径中显式调用 cancelBuffer() 归还Buffer。
  • 使用智能指针:在HAL实现中使用 sp<GraphicBuffer> 自动管理引用计数。
  • 监控工具:利用 dumpsys 或 procrank 监控Buffer分配与泄漏情况。
性能优化要点: 在Camera HAL中,建议将Buffer分配与引用计数操作放在同一线程上下文中,避免跨线程竞争导致计数异常。对于高频场景(如60fps预览),使用无锁原子操作(如 __sync_fetch_and_add)更新引用计数。

3.5 本章小结

Gralloc Buffer管理是Camera HAL Pipeline数据传输优化的基础。通过理解Buffer的分配流程、生命周期状态转换以及引用计数机制,开发者可以避免常见的内存泄漏与帧率抖动问题。在实际优化中,应重点关注Buffer预分配策略、引用计数配对调用以及多消费者共享场景下的计数管理。