c# 处理大批量数据 时遇到 类型为“System.OutOfMemoryException”的异常

本文讲述了在C#中处理大量数据时遇到内存溢出异常的问题及解决方案。由于32位系统内存限制,作者选择通过分批加载和优化SQL查询来避免一次性加载数据到DataTable。通过在内存中分批处理数据并使用Exists关键字优化连表查询,提高处理效率。最终将处理后的数据批量插入到目标数据库,并建议将方法封装以提高复用性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

因为业务需要在给客户处理多张表中均存在100W以上数据的时候出现如题错误

原因是在将数据装入DataTable中时所占用内存太大导致

在网上查了下原因后是因为32位软件所能使用的内存有限,本想将软件移植到64位机器上去运行,可因为软件本身所牵涉的32位系统组件过多而放弃

还是打算从软件本身的算法入手


先简单讲下软件的需求吧:总的来说就是要把客户的以前使用过的软件的oracle数据转入到现在我们的软件数据库中,现在他们不用上一家的软件了:)

而这个转换过程中会用到连表这类的操作,所以在百W级数据中进行多张表的连表操作速度是非常慢的,即使我们在数据转换过程中只需要取一次数据就可以了,但是我测算了下,在建立好所有必要的索引后,要将5、6张表的数据进行连接这个操作起码得耗上1、2个小时,当然这还必须是要安装64位的oracle,可因为我之前说过我们的软件用了太多32位系统的组件导致无法采用64位这种模式,所以同学们如果可以的话以后尽量使用64位软件吧,那速度肯定是杠杠的。


好,说完了大致的需求以及问题后,应该寻找解决方案了,为了能使得这个方案显得更加自然,我准备把我寻找该方案的过程也说明一下。

既然不能将所有数据一次性加载到DataTable中,那我们想到当然就是分批加载,做一个类似分页的方法,在这个过程中遇到同样的问题,那就是即使做了分页,在多表数据连接的时候还是会非常非常慢,那这个分页的意义就不是很大了,这时我在考虑是否能先分批把所有用到的表的数据取出来(每个表对

<think>我们正在处理一个C#服务连接SQL Server不定System.OutOfMemoryException的问题。根据提供的引用信息,我们可以总结出几个可能的原因和解决办法。 可能的原因: 1. 内存泄漏:程序未能正确释放不再使用的内存,导致内存占用逐渐增加,最终引发OutOfMemoryException。在定服务中,如果每次执行任务后没有释放资源(如数据库连接、对象等),就容易出现这种情况。 2. 数据处理:在处理数据,如果没有分页或分批处理,一次性加载所有数据到内存中,可能导致内存不足(引用[4]提到处理大批量数据遇到此异常)。 3. 资源未及释放:如数据库连接、文件流、网络连接等未及关闭,导致资源耗尽。 4. 32位进程内存限制:如果程序运行在32位环境下,进程可用内存通常被限制在2GB以内(或通过设置可扩展到3GB),当内存使用超过这个限制就会抛出异常。 5.间的运行导致内存碎片化:程序长间运行后,内存中可能存在量碎片,无法分配块连续内存。 解决方案: 1. **检查内存使用情况**:使用内存分析工具(如Visual Studio的内存分析器、ANTS Memory Profiler、DotMemory等)分析内存使用情况,找出内存泄漏点。 2. **优化数据访问**: - 对于数据量查询,使用分页(如`OFFSET FETCH`)或分批处理(如循环分批读取)来减少单次操作的内存占用(引用[4]中提到分页处理)。 - 使用`SqlDataReader`逐行读取数据,而不是一次性将所有数据加载到内存(如使用`DataTable`或`DataSet`)。 - 确保及关闭数据库连接(使用`using`语句包裹`SqlConnection`和`SqlCommand`等)。 3. **释放资源**: - 确保所有实现了`IDisposable`接口的对象(如`SqlConnection`, `SqlCommand`, `Stream`等)都被正确释放(使用`using`语句或在`finally`块中释放)。 - 检查是否有静态变量持有对象的引用,导致无法被垃圾回收。 4. **增加可用内存**: - 如果可能,将程序改为64位运行(需要确保系统是64位,并且程序编译为64位目标平台),这样可以使用更多的内存。 - 调整系统虚拟内存设置。 5. **优化代码**: - 避免频繁创建对象,考虑重用对象(使用对象池)。 - 及将不再需要的对象设置为`null`,以便垃圾回收器回收(但通常不是必须的,因为垃圾回收器会自动管理,但在长期存活的对象中引用对象有用)。 6. **定期重启服务**:如果问题难以定位,可以设置一个定期重启服务的机制(如引用[3]中提到的,服务端运行一周左右出现异常后重启即可正常运行),但这只是临解决方案。 具体到定服务,我们应重点检查定任务执行过程中的资源释放情况,特别是数据库连接和查询结果的处理。 步骤建议: 1. 检查定任务执行代码,确保所有数据库连接、命令、读取器等都被包裹在`using`语句中。 ```csharp using (SqlConnection conn = new SqlConnection(connectionString)) { conn.Open(); using (SqlCommand cmd = new SqlCommand(query, conn)) { using (SqlDataReader reader = cmd.ExecuteReader()) { // 处理数据 } } } ``` 2. 检查是否在定任务中处理数据。如果是,考虑分批处理。例如,每次处理1000条记录。 3. 使用性能计数器监控内存使用情况,观察内存增长趋势。 4. 检查是否有其他资源(如文件、网络流等)未释放。 如果问题仍然存在,使用内存分析工具进行深入分析。 相关问题: 1. 如何监控C#程序的内存使用情况? 2.C#中,如何正确释放数据库连接资源? 3. 如何处理数据量的数据库查询以避免内存溢出? 4. C#程序在32位和64位环境下的内存限制有何不同? 5. 如何设置定服务的自动重启机制?</think>### C#服务连接 SQL Server 报 System.OutOfMemoryException 的解决方法 此问题通常由**内存泄漏**或**资源未释放**引起,尤其在长间运行的定服务中。以下是系统性的原因分析和解决方案: --- #### **一、核心原因分析** 1. **数据库连接泄漏** - 未正确关闭 `SqlConnection`/`SqlCommand`,导致连接池耗尽并持续占用内存[^3]。 - 验证方法:监控SQL Server的 `sys.dm_exec_sessions` 视图,观察连接数是否持续增长。 2. **数据处理不当** - 一次性加载数据到内存(如未分页查询),触发内存溢出[^4]。 - 示例危险代码: ```csharp var data = new DataTable(); using (var adapter = new SqlDataAdapter("SELECT * FROM HugeTable", connection)) { adapter.Fill(data); // 可能加载百万级数据 } ``` 3. **未释放非托管资源** - 未及释放 `IDisposable` 对象(如文件流、网络流),导致内存碎片化[^1]。 4. **32位进程内存限制** - 32位应用程序默认内存上限为 **2GB**(即使物理内存充足)[^1]。 --- #### **二、针对性解决方案** ##### 1. **修复数据库连接泄漏** ```csharp // ✅ 正确做法:使用嵌套 using 确保资源释放 using (SqlConnection conn = new SqlConnection(connString)) { conn.Open(); using (SqlCommand cmd = new SqlCommand("SELECT ...", conn)) { using (SqlDataReader reader = cmd.ExecuteReader()) { while (reader.Read()) { // 处理单行数据 } } // reader 自动关闭 } // cmd 自动释放 } // conn 自动关闭并返回连接池 ``` > **关键点**:避免在循环中创建连接,改用连接池复用机制[^3]。 ##### 2. **优化数据处理** - **分页加载数据**: ```csharp int pageSize = 1000; for (int pageIndex = 0; ; pageIndex++) { string sql = $@" SELECT * FROM LargeTable ORDER BY ID OFFSET {pageIndex * pageSize} ROWS FETCH NEXT {pageSize} ROWS ONLY"; using (var reader = cmd.ExecuteReader()) { if (!reader.HasRows) break; // 处理当前页数据 } } ``` - **使用 `SqlDataReader` 流式处理**: 逐行读取数据,避免一次性加载到 `DataTable`。 ##### 3. **释放非托管资源** - 显式调用 `Dispose()` 或使用 `using` 语句处理以下对象: - `FileStream`、`MemoryStream` - `Bitmap`、`Graphics` - 网络相关对象(如 `HttpClient` 需单例复用) ##### 4. **突破32位内存限制** - 将项目编译目标改为 **x64**: `项目属性 > 生成 > 目标平台 = x64` (64位进程可访问超过4GB内存) ##### 5. **内存监控与回收** ```csharp // 在定任务结束强制垃圾回收(谨慎使用) if (DateTime.Now.Hour == 3) // 在低峰期触发 { GC.Collect(); GC.WaitForPendingFinalizers(); } ``` > **注意**:仅在确认内存增长异常使用,避免频繁GC影响性能。 --- #### **三、预防性措施** 1. **内存分析工具** - 使用 [**Visual Studio Diagnostic Tools**](https://learn.microsoft.com/zh-cn/visualstudio/profiling/memory-usage) 或 [**JetBrains dotMemory**](https://www.jetbrains.com/dotmemory/) 定期检测内存泄漏点。 2. **连接池配置优化** ```xml <connectionStrings> <add name="MyDB" connectionString="Server=...;Max Pool Size=200;Connection Timeout=30;" /> </connectionStrings> ``` - 设置合理的 `Max Pool Size`(默认100)和 `Connection Timeout`[^2]。 3. **服务自愈机制** ```csharp // 捕获异常后自动重启服务 try { RunScheduledTask(); } catch (OutOfMemoryException ex) { LogError(ex); Environment.Exit(1); // 被监控进程重启 } ``` --- #### **四、验证方法** 1. 监控内存工具: ```powershell # 实查看进程内存 Get-Process -Name YourService | Format-Table WS, VM ``` 2. SQL Server 连接数检查: ```sql SELECT COUNT(session_id) AS ActiveConnections, login_name FROM sys.dm_exec_sessions WHERE database_id = DB_ID('YourDB') GROUP BY login_name; ``` > **总结**:通过**资源释放优化** + **分页处理** + **64位迁移** 可解决90%的此类异常。若问题仍出现,需用内存分析工具定位泄漏点[^1][^3]。 --- ###
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值