关键在于用结构体+连续数组替代list,实现内存物理连续、免gc、零拷贝;优先用point3d[]等struct数组,配合span切片、预分配索引管理及内存池优化。

直接把大批量捕获对象从 List<t></t> 搬到连续一维数组(如 T[] 或 Span<t></t>),不是简单“换容器”,而是绕过托管堆频繁分配、GC扫描和缓存不友好的关键一步。真正起效的核心,在于让数据在内存中物理连续、访问可预测、操作免拷贝。
优先用结构体 + 数组替代类对象列表
如果捕获对象本身是轻量级、不可变或生命周期明确的数据(比如坐标点、传感器采样值、帧像素数据),应定义为 struct 而非 class:
- 结构体默认栈分配或内联在数组中,避免每个对象单独堆分配
-
T[]数组天然连续,CPU 缓存预取高效,遍历速度通常比List<t></t>快 2–3 倍 - 示例:
Point3D[] points = new Point3D[100000];—— 所有 10 万点紧挨着排布,无指针跳转
用 Span 零拷贝切片与复用
若需对原始数据做子集处理(如只分析某帧中间 ROI 区域),别再 ToArray() 或 GetRange():
Span<t> span = points.AsSpan();</t>-
Span<t> roi = span.Slice(startIndex, length);</t>—— 不分配新内存,仅生成轻量视图 - 可在循环中反复复用同一块数组 + 不同
Span视图,彻底消除 GC 压力
预分配固定大小数组 + 索引管理代替动态扩容
List<t></t> 的 Add() 在容量不足时触发数组翻倍复制,O(n) 开销累积明显。重构策略是:
- 根据业务上限(如每秒最多捕获 5000 个对象),一次性分配足额数组:
var buffer = new CaptureItem[5000]; - 用整型计数器
_count追踪当前有效长度,写入时直接索引赋值:buffer[_count++] = item; - 后续处理统一基于
buffer.AsSpan(0, _count),安全且零开销
配合内存池应对高频创建/销毁场景
若捕获对象虽为 class 但大小固定、复用率高(如网络包解析后的 PacketHeader),可结合对象池:
- 池预先分配大块连续内存(如
byte[] poolBuffer = new byte[1024 * 1024];) - 用
Span<byte></byte>划分区域,配合Unsafe.AsRef<t></t>定位对象首地址 - 避免
new T()触发堆分配,实测单位创建耗时可从 1500ns 降至 400ns










