std::vector 最佳实践 — 长期反复使用的 vector 内存稳定策略

0. 背景

生产上碰到程序内存持续上升,且无异常,表现为观察 VmRSS 不断增加。经多轮分析调研后,定位为 std::vector 的容量问题。特提出最佳实践,予以记录。

1. 核心规则

长期反复使用的 vector,必须在创建完后立即 reserve(合理大小),保证内存相对稳定。

2. 根因

std::vector::clear() 只清空元素,不释放 capacity:

std::vector<int> vec;
for (int i = 0; i < 10000; i++) vec.push_back(i);
// capacity ≈ 16384

vec.clear();
// size = 0, 但 capacity 仍然 = 16384, 内存未释放

capacity 的行为特征:

操作 对 capacity 的影响
push_back 超出 capacity 扩容 (约 2x)
clear() 不变
erase() 不变
resize(n) 当 n < capacity 不变
reserve(n) 当 n > capacity 扩至 n
reserve(n) 当 n ≤ capacity 不变
shrink_to_fit() 缩至 size (非强制, 实现定义)
swap 技巧 缩至 size

结论:capacity 只升不降(shrink_to_fit 除外),是内存高水位的直接原因。

3. 反复 clear + push_back 的问题

// 典型 WorkAction 参数更新周期
std::vector<SysTime> list;
while (running) {
list.clear(); // size=0, capacity 保持高位
for (auto& item : new_data)
list.push_back(item); // 可能触发扩容, capacity 继续增长
}

每次更新元素数量波动:

更新1: add 500   → capacity 扩至 512
更新2: add 1200 → capacity 扩至 2048 (新高)
更新3: add 300 → capacity 保持 2048 (浪费 1748 slots)
更新4: add 2500 → capacity 扩至 4096 (新高)
更新5: add 800 → capacity 保持 4096 (浪费 3296 slots)

capacity 取历史最大值,永不回落。

4. 解决方案:创建后立即 reserve

4.1 基本模式

std::vector<SysTime> list;
list.reserve(MAX_EXPECTED_SIZE); // 创建后立即 reserve

while (running) {
list.clear(); // size=0, capacity=MAX_EXPECTED_SIZE (不变)
for (auto& item : new_data)
list.push_back(item); // 不超出 capacity, 无扩容
}

4.2 reserve 大小选择

策略 reserve 值 优点 风险
取业务峰值 历史最大元素数 完全消除扩容 峰值过大时浪费
取峰值 × 1.2 留 20% 余量 覆盖突发增长 略多占用
取平均 × 2 覆盖多数场景 平衡 偶尔仍需扩容

4.3 实验验证

std::vector Node2K (1992B) 测试:

模式 轮次 RSS 变化 drift
reserve(1000) + clear+add 10000 3,484 → 7,372 KB (稳定) 0 KB
不 reserve + clear+add 10000 capacity 增至 2048, RSS 不回落 持续增长
100实例 × reserve(1000) 1000 RSS 稳定在 392,548 KB 0 KB

固定 capacity 后,10000 轮迭代 RSS 完全稳定,drift = 0。

5. 配合策略

元素数不可预估时:clear + shrink_to_fit + reserve

while (running) {
list.clear();
list.shrink_to_fit(); // 回收 capacity
int estimated = estimate_next();
list.reserve(estimated); // 按需重新 reserve
for (auto& item : new_data)
list.push_back(item);
}

适用于:每次更新前可预估元素数量的场景。

6. 适用范围

此规则适用于所有基于 std::vector 的封装:

7. 规则总结

  1. 创建 vector 后立即 reserve(合理大小)
  2. reserve 时机: 对象构造后、首次使用前
  3. reserve 大小: 取业务峰值或略高, 宁可多 20% 不可少
  4. 清空后若需释放: clear() + shrink_to_fit()
  5. 不可预估时: clear() + shrink_to_fit() + reserve(预估值)
  6. 绝对禁止: 反复 clear + push_back 而不 reserve

注意

shrink_to_fit 可能会造成内存碎片,但不会导致 RSS growth

8. 测试数据

测试 模式 10000轮后 RSS drift
A 固定 capacity, clear+add 5,424 KB (固定) 0 KB
B 固定 capacity, clear+shrink+add 7,416 KB (固定) 0 KB
C 不固定 capacity (对照) 7,496 KB capacity 增至 2048
D 100实例 × 固定 capacity × 1000轮 198,012 KB (固定) 0 KB