Memory Budgeting
本技能的目标是让内存成为一个被测量的预算,而不是一个板子上的标称标签。实现基于嵌入式 C/C++;当确定性工具更有用时,脚本可以解析构建日志或运行时日志。
适用场景
当 Arduino 或嵌入式项目触及 SRAM、Flash、堆(heap)、栈(stack)、分区(partition)、JSON 缓冲区或性能限制时使用;也适用于检查 String 的使用、F() 的放置位置、OTA 槽位、FreeRTOS 栈或内存余量(memory margin)。
信息摄取(Intake)
需要记录:确切的开发板/模块、Flash/RAM/EEPROM/PSRAM 容量、引导程序(bootloader)与分区布局、所用框架、编译器标志、依赖库、静态缓冲区、任务栈、文件系统/OTA 需求、峰值协议大小,以及可用的测量输出。
处理流程(Process)
- 分离静态数据、代码/Flash、堆、任务栈、中断状态、文件系统、引导程序以及 OTA/更新槽位。
- 为每一类建立数值预算与最小余量。应使用具体开发板的参考值,而不是把属于整个系列的数字直接当作某个项目的结论。
- 采集编译器体积输出,并把变化映射到某个功能或依赖。
- 在框架提供相应能力的前提下,测量代表性负载下的运行时可用堆、最大可用块、栈高水位标记(high-water marks)以及复位/分配失败情况。
- 在 AVR 上,适当时用
F()把字面量保留在 Flash 中,优先使用有界缓冲区与固定大小存储,避免无界动态String使用。在 ESP32/RP2040 上,仍需对缓冲区设界,并区分堆、栈、Flash 与文件系统。 - 每次影响内存的改动之后,重新运行压力用例,并记录其取舍。
反合理化对照(Anti-rationalization)
| 走捷径的说法 | 应对 |
|---|---|
| “数据手册说 520 KB,所以内存没问题。” | 区分可用堆、栈、保留区域,以及射频/RTOS 占用。 |
| “它编译通过了。” | 检查体积输出,并实际跑一遍运行时分配路径。 |
| “把缓冲区调大一点。” | 先给协议设界,并证明所需的最大值。 |
| “到处都用 String。” | 检查碎片化与生命周期;必要时改用有界替代方案。 |
| “OTA 只需要应用镜像。” | 要为两个更新槽位、引导程序、元数据和回滚镜像都做预算。 |
验证(Verification)
- 构建体积输出与运行时内存证据都要指明确切的硬件目标/版本。
- 每个预算都要有余量与失败时的应对方案。
- 压力测试要覆盖峰值消息、传感器、日志、射频与任务负载(视适用情况而定)。
- 优化不得移除边界检查、安全检查或恢复行为。
共享输出契约
使用共享的 Arduino 技能契约(../../docs/arduino-skill-contract.md):说明假设、所需工具与版本、实现步骤、按证明阶段划分的测试/证据、已知限制,以及恢复/安全注意事项。