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
相关产品推荐
相关产品推荐

