C#中File.ReadLines()读取文件内存占用过高如何解决
问题原因与解决方案
你遇到的内存占用过高告警,绝大多数情况不是File.ReadLines()或StreamReader的实现问题,属于检测工具告警阈值敏感+运行时正常内存开销导致的误判,可按以下步骤排查处理:
1 基础认知说明
20个总大小200MB的txt文件逐行读取时,进程出现几十MB的内存占用属于完全正常的范围:
- .NET 6.0 控制台应用启动本身就会占用20-40MB的基础托管内存,包含运行时、类型系统、已加载程序集的固定开销
- 所有文件读取API都会分配固定大小的IO缓冲区做磁盘读写、编码转换,默认缓冲区大小在4KB到4MB不等
- 逐行读取会产生大量字符串对象,在GC未触发回收前,这些对象会暂时留在托管堆上,不代表内存泄漏
你查阅的资料描述是准确的:File.ReadLines()确实是逐行枚举返回内容,不会一次性把整个文件加载到内存。你换成StreamReader也出现相同内存表现,恰恰说明不是读取API的问题——File.ReadLines()内部本身就是通过StreamReader实现逐行读取的。
2 验证是否为真实内存异常
Rider DPA的内存告警是基于预设阈值触发的,不代表真的存在内存问题。你可以在测试代码中加入手动GC调用,验证内存是否能正常回落:
var directory = "path/to/files"; foreach (var filepath in Directory.EnumerateFiles(directory, "*.txt")) { foreach (var line in File.ReadLines(filepath)) { // 后续补充业务处理逻辑 } // 仅测试用,生产环境不要添加强制GC代码 GC.Collect(); GC.WaitForPendingFinalizers(); GC.Collect(); }
如果执行完上述代码后,进程内存稳定在30-60MB区间,就属于完全正常的表现,不需要做任何额外处理。你当前因为业务逻辑为空,循环执行速度极快,每秒会生成数十万临时字符串对象,堆积速度超过GC默认触发阈值,才会显示瞬时内存峰值高,等实际业务逻辑加入后循环速度下降,或者GC自动触发后内存就会回落。
3 确需降低内存占用的优化方案
如果验证后发现内存确实持续上涨不回落,或者需要进一步压低内存占用,可以按以下方式调整:
- 关闭服务器GC模式:64位.NET Core应用默认可能开启服务器GC,该模式会为每个CPU核心分配独立堆段,内存回收策略更倾向于预留内存给后续计算,不会及时把内存归还给操作系统,内存占用会明显更高。可在项目csproj文件中加入如下配置关闭:
<PropertyGroup> <ServerGarbageCollection>false</ServerGarbageCollection> </PropertyGroup>
- 手动指定更小的读取缓冲区:默认
File.ReadLines()和无参StreamReader会分配较大的IO缓冲区,可以手动初始化StreamReader指定更小的缓冲区降低固定开销:
foreach (var filepath in Directory.EnumerateFiles(directory, "*.txt")) { // 指定1KB缓冲区,可根据实际业务调整大小 using var reader = new StreamReader(filepath, Encoding.UTF8, true, bufferSize: 1024); string? line; while ((line = reader.ReadLine()) != null) { // 业务处理逻辑 } }
- 避免持有已处理行的引用:后续编写业务逻辑时,不要把逐行读取到的
line字符串存入全局生命周期的列表、字典等容器中,否则无论用什么读取方式都会出现内存持续上涨的问题。
内容的提问来源于stack exchange,提问作者Ellisein
相关产品推荐
相关产品推荐

