ParallelProcessor中创建DataTable触发OutOfMemoryException求助
问题背景
在并行处理器(ParallelProcessor)中封装创建DataTable并加载单行数据的逻辑,将其作为表类型参数使用时,部分记录可正常更新,部分触发OutOfMemoryException,报错栈指向System.Data.RBTree1`的内存分配环节,且该问题仅在部分环境出现。
报错信息:
System.OutOfMemoryException: Exception of type 'System.OutOfMemoryException' was thrown.
at System.Data.RBTree1.TreePage..ctor(Int32 size) at System.Data.RBTree1.AllocPage(Int32 size)
at System.Data.RBTree`1.GetNewNode(K key)
at System.Data.DataTable.SetNewRecordWorker(DataRow row, Int32 proposedRecord, DataRowAction action, Boolean isInMerge, Boolean suppressEnsurePropertyChanged, Int32 position, Boolean fireEvent, Exception& deferredException)
at System.Data.DataTable.InsertRow(DataRow row, Int64 proposedID, Int32 pos, Boolean fireEvent)
可能原因
- 并行任务内存堆积:短时间内大量并行任务创建DataTable实例,每个实例即使仅存单行数据,也包含列定义、内部索引结构等固定开销,累积后超出系统可用内存阈值。
- 环境配置差异:不同环境的可用内存、GC模式(工作站GC/服务器GC)、线程并发限制不同,部分环境的资源不足以支撑当前并行规模。
- 资源未及时回收:并行场景下GC回收不及时,已使用的DataTable实例未被快速释放,导致内存占用持续攀升。
解决方案
1. 限制并行并发量
调整ParallelProcessor的最大并发线程数,避免短时间内创建过多DataTable实例,降低内存瞬时压力。
2. 复用DataTable实例
通过对象池复用已初始化的DataTable,避免重复创建带来的内存开销:
// 实现简单的DataTable对象池 using Microsoft.Extensions.ObjectPool; private static readonly ObjectPool<DataTable> _dataTablePool = new DefaultObjectPool<DataTable>(new DataTablePoolPolicy()); private class DataTablePoolPolicy : IPooledPolicy<DataTable> { public DataTable Create() { var table = new DataTable(); table.Columns.Add("ID", typeof(int)); return table; } public bool Return(DataTable table) { table.Rows.Clear(); return true; } } // 业务方法中使用对象池 public DataTable GetRecords(List<testData> testData) { var table = _dataTablePool.Get(); foreach (var item in testData.Select(x => x.ID)) { var dr = table.NewRow(); dr["ID"] = item; table.Rows.Add(dr); } // 注意:需在DataTable作为参数使用完毕后,调用以下代码归还到池 // _dataTablePool.Return(table); return table; }
3. 替换为轻量级数据载体
若仅需传递单行数据,直接使用自定义实体类或匿名对象替代DataTable,彻底消除DataTable的额外内存开销;若必须使用表类型参数,可考虑使用流式DataReader(如SqlDataReader)传递数据,减少内存占用。
4. 优化GC配置
在服务器环境启用服务器GC,提升并行场景下的内存回收效率,修改配置文件:
<configuration> <runtime> <gcServer enabled="true"/> </runtime> </configuration>
5. 控制任务队列积压
确保ParallelProcessor的任务队列不会积压过多未执行任务,避免内存占用持续增长。
内容的提问来源于stack exchange,提问作者Gladstone Jaffrey

