You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

.NET Framework4.6.1读取超2GB TXT时抛出OutOfMemoryException异常

问题根因

你遇到的OutOfMemoryException和64位系统没有直接关系,核心是当前实现的内存占用模型完全不支持大文件处理:

  • ReadToEnd()会一次性将全量文件内容加载为字符串,.NET中字符串默认是UTF-16编码,如果原文件是UTF-8编码的2GB文件,仅这一步就需要至少4GB的连续内存
  • 后续Split()拆分行数组、StringBuilder拼接输出内容、最终ToString()生成输出字符串,会产生多份全量数据拷贝,峰值内存会达到原文件体积的4~6倍,2GB文件峰值内存需求超过10GB
  • .NET Framework 4.6.1默认对大对象堆(LOH)有分配限制,且即使是64位进程,内存碎片也很容易导致找不到足够大的连续内存块触发OOM。
第一步:放开基础内存限制(临时缓解方案)

如果暂时不想重构代码,可以先调整项目配置拉高内存上限:

  • 右键项目→【属性】→【生成】,取消勾选首选32位,平台目标选择x64或Any CPU
  • 在项目的App.config中添加如下运行时配置,开启大对象支持:
<configuration>
  <runtime>
    <!-- 允许总大小超过2GB的数组等大对象 -->
    <gcAllowVeryLargeObjects enabled="true" />
    <!-- 多核环境下开启服务器GC优化内存分配效率 -->
    <gcServer enabled="true"/>
  </runtime>
</configuration>

注意:这个方案只能提升可处理的文件体积上限,面对更大的文件依然会触发OOM,且内存占用极高,不是长久方案。

第二步:重构为流式逐行处理(根治方案)

你的业务逻辑本身是按行维度处理的,完全不需要加载全量文件,只需要维护一个最多存2行的缓存队列,就可以实现内存占用恒定(仅数MB,和文件大小无关)的处理,同时避免原文件损坏风险:

  • 逐行读取原文件,用队列缓存最近读到的2行
  • 每读到新行时,将队列中最早存入的行按普通规则判断处理写入临时文件
  • 全部行读取完成后,队列中剩余2行分别对应原文件的倒数第二行、最后一行:倒数第二行按特殊规则处理写入,最后一行直接丢弃
  • 所有内容写入临时文件成功后,用临时文件替换原文件

优化后的完整实现代码如下:

public static void ModifyFile(string directory, string filename)
{
    string filePath = Path.Combine(directory, filename);
    string tempFilePath = Path.Combine(directory, filename + ".tmp");
    int counter = 1;
    // 用队列缓存最多2行,用于识别最后两行
    Queue<string> lineBuffer = new Queue<string>(2);

    // 逐行读原文件,逐行写临时文件
    using (StreamReader reader = new StreamReader(filePath))
    using (StreamWriter writer = new StreamWriter(tempFilePath, false, reader.CurrentEncoding))
    {
        string currentLine;
        while ((currentLine = reader.ReadLine()) != null)
        {
            lineBuffer.Enqueue(currentLine);
            // 缓存满2行后,取出最早的一行按普通规则处理
            if (lineBuffer.Count > 2)
            {
                string processLine = lineBuffer.Dequeue();
                // 判断第121位开始的2个字符是否为NA,加长度校验避免越界
                if (processLine.Length >= 123 && processLine.Substring(121, 2).Equals("NA"))
                {
                    string processed = ManipulateStringElement(processLine, counter, 22);
                    writer.WriteLine(processed);
                    counter++;
                }
            }
        }

        // 文件读完后,处理队列里的倒数第二行(队列第一个元素)
        if (lineBuffer.Count >= 2)
        {
            string secondLastLine = lineBuffer.Dequeue();
            string processed = ManipulateStringElement(secondLastLine, counter, 22);
            // 倒数第二行按原逻辑不追加额外换行
            writer.Write(processed);
        }
        // 队列最后一个元素是原文件最后一行,直接丢弃
    }

    // 处理完成后替换原文件
    File.Delete(filePath);
    File.Move(tempFilePath, filePath);
}

private static string ManipulateStringElement(string item, int counter, int start)
{
    if (item.Length < start + 9)
        return item; // 短行直接返回避免越界
    // 按位置精准替换,避免原Replace逻辑误替换行内其他相同字符串
    return item.Remove(start, 9).Insert(start, GenerateProgressive(counter));
}

private static string GenerateProgressive(int counter)
{
    return counter.ToString().PadLeft(9, '0');
}
额外优化点说明
  • 移除了原逻辑中冗余的换行符替换:StreamReader.ReadLine()会自动剥离行尾的\r/\n/\r\n,写入时用WriteLine统一输出系统标准换行,不会出现换行符混乱问题
  • 移除了冗余的Close()调用:using代码块会在资源离开作用域时自动释放流,不需要手动调用关闭
  • 增加了行长度校验,避免原文件存在格式错误的短行时触发ArgumentOutOfRangeException
  • 采用临时文件写入+最终替换的逻辑,避免处理中途程序崩溃导致原文件被清空损坏
  • 去掉了原逻辑中无意义的全量字符串Split、全量StringBuilder拼接,内存占用和文件大小完全解耦,哪怕是几十GB的文件也可以正常处理
  • 修正了原逻辑中用Replace替换指定位置内容可能误替换行内其他相同字符串的bug,改为按位置精准移除插入

内容的提问来源于stack exchange,提问作者Marduk

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.26 10:03:21