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

C#合并多个大JSON文件时遭遇System.OutOfMemoryException的解决办法

解决大JSON合并内存溢出问题的可行方案

你当前的代码虽然用了JsonTextReader,但还是把每个JSON文档完整解析成JToken并存储在列表中,最后还要合并成大JArray再序列化——等于所有JSON数据都同时驻留在内存里,这才是内存溢出的核心原因。以下是针对性的解决方案:

方案1:完全流式读写(最优解)

直接跳过将整个JSON加载到内存的步骤,一边读取输入文件,一边写入输出文件,全程只处理当前读取的JSON片段,内存占用极低。

示例代码:

using System;
using System.IO;
using Newtonsoft.Json;

public class Program
{
    public static void Main()
    {
        string directoryPath = @"C:\test\Directory";
        string outputPath = @"C:\test\cons.json";
        string[] jsonFiles = Directory.GetFiles(directoryPath, "*.json");

        using (var outputWriter = new StreamWriter(outputPath))
        using (var jsonWriter = new JsonTextWriter(outputWriter))
        {
            // 写入数组开头
            jsonWriter.WriteStartArray();

            for (int i = 0; i < jsonFiles.Length; i++)
            {
                using (var inputReader = new StreamReader(jsonFiles[i]))
                using (var jsonReader = new JsonTextReader(inputReader))
                {
                    // 直接复制当前JSON内容到输出,不解析成JToken
                    jsonWriter.WriteToken(jsonReader);
                }

                // 非最后一个文件时添加逗号分隔
                if (i != jsonFiles.Length - 1)
                {
                    jsonWriter.WriteRaw(",");
                    jsonWriter.WriteWhitespace(Environment.NewLine);
                }
            }

            // 写入数组结尾
            jsonWriter.WriteEndArray();
        }
    }
}

核心是JsonWriter.WriteToken(jsonReader),它直接将输入流的JSON内容复制到输出流,全程不会把整个JSON加载到内存,内存占用仅取决于当前读取的片段大小。

方案2:按需解析并流式输出(需修改JSON内容时用)

如果合并前需要对单个JSON做字段修改、过滤等操作,可以在流式读取过程中逐个处理节点,再写入输出,同样不加载整个文档:

示例代码:

using System;
using System.IO;
using Newtonsoft.Json;

public class Program
{
    public static void Main()
    {
        string directoryPath = @"C:\test\Directory";
        string outputPath = @"C:\test\cons.json";
        string[] jsonFiles = Directory.GetFiles(directoryPath, "*.json");

        using (var outputWriter = new StreamWriter(outputPath))
        using (var jsonWriter = new JsonTextWriter(outputWriter))
        {
            jsonWriter.WriteStartArray();

            for (int i = 0; i < jsonFiles.Length; i++)
            {
                using (var inputReader = new StreamReader(jsonFiles[i]))
                using (var jsonReader = new JsonTextReader(inputReader))
                {
                    // 逐个读取节点并处理
                    while (jsonReader.Read())
                    {
                        // 示例:将"oldField"重命名为"newField"
                        if (jsonReader.TokenType == JsonToken.PropertyName && jsonReader.Value.ToString() == "oldField")
                        {
                            jsonWriter.WritePropertyName("newField");
                            jsonReader.Read();
                            jsonWriter.WriteToken(jsonReader);
                        }
                        else
                        {
                            jsonWriter.WriteToken(jsonReader);
                        }
                    }
                }

                if (i != jsonFiles.Length - 1)
                {
                    jsonWriter.WriteRaw(",");
                    jsonWriter.WriteWhitespace(Environment.NewLine);
                }
            }

            jsonWriter.WriteEndArray();
        }
    }
}

方案3:大对象堆优化(辅助手段)

如果必须使用JToken处理复杂合并逻辑,可以通过配置优化大对象堆(LOH),减少内存碎片缓解压力:

在app.config或web.config中添加:

<runtime>
  <gcAllowVeryLargeObjects enabled="true" />
  <gcServer enabled="true" />
  <gcConcurrent enabled="true" />
</runtime>

注意:这只是辅助优化,无法从根本解决“全量数据驻留内存”的问题,仅适合JSON总大小接近内存上限的场景。

额外注意事项

  • 绝对避免使用File.WriteAllText或JToken.ToString(),这类方法会把整个JSON序列化成单个字符串,瞬间占用大量内存;
  • 优先选择流式API处理大文件,这是大体积数据处理的标准范式;
  • 如果合并后的JSON超过2GB,必须启用gcAllowVeryLargeObjects,否则会触发内存异常。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.10 03:09:55