Newtonsoft.Json反序列化时偶发内存溢出问题求助
解决单行大JSON反序列化时的OutOfMemory异常
这个问题我之前帮同事排查过类似的情况,咱们来拆解下原因和可行的解决方案:
为什么会出现这个问题?
虽然你的JSON文件只有20MB,但**JObject会把整个JSON结构完整加载到内存中**,而单行大JSON的解析过程会产生大量临时对象,再加上JToken(JObject的基础单元)本身的对象开销(每个字段、值都是独立的JToken实例),实际内存占用峰值可能远大于文件本身的大小。偶尔触发异常的原因,大概率是程序运行时的内存碎片、系统可用内存波动导致的——刚好在临界值时就会爆内存。
解决方案1:逐节点解析(最推荐,内存占用极低)
如果你不需要一次性操作整个JSON树,完全可以用JsonTextReader逐节点读取,只处理你需要的部分,这样内存占用会降到最低。示例代码如下:
private void ProcessLargeJson(string pathToFile) { try { using (FileStream s = new FileStream(pathToFile, FileMode.Open, FileAccess.Read)) using (StreamReader sr = new StreamReader(s)) using (JsonTextReader reader = new JsonTextReader(sr)) { while (reader.Read()) { // 举个例子:只处理名为"criticalField"的属性值 if (reader.TokenType == JsonToken.PropertyName && reader.Value.ToString().Equals("criticalField", StringComparison.OrdinalIgnoreCase)) { reader.Read(); // 移动到属性对应的数值节点 var fieldValue = reader.Value; // 在这里处理你的业务逻辑 Console.WriteLine($"Found value: {fieldValue}"); } } } } catch (Exception exc) { _log.Error($"Error processing JSON file: {exc}"); } }
解决方案2:优化JObject加载的内存占用
如果业务上必须加载整个JSON到JObject,可以通过调整JsonSerializer的配置来减少内存开销:
private JObject ReadJsonFileOptimized(string pathToFile) { JObject jsonObject = null; try { using (FileStream s = new FileStream(pathToFile, FileMode.Open, FileAccess.Read)) using (StreamReader sr = new StreamReader(s)) using (JsonReader reader = new JsonTextReader(sr)) { JsonSerializer serializer = new JsonSerializer { // 禁用引用保留,减少额外内存开销 PreserveReferencesHandling = PreserveReferencesHandling.None, // 忽略循环引用(如果你的JSON存在的话) ReferenceLoopHandling = ReferenceLoopHandling.Ignore, // 忽略元数据属性(如果JSON中包含的话) MetadataPropertyHandling = MetadataPropertyHandling.Ignore }; jsonObject = serializer.Deserialize<JObject>(reader); } } catch (Exception exc) { _log.Error($"Error reading file: {exc}"); } return jsonObject; }
解决方案3:检查程序位数(容易忽略的点)
如果你的程序是32位编译,那么它的内存上限大概只有2GB左右,即使JSON加载后只占几百MB,加上程序本身的内存使用,很容易触碰到上限。改成64位编译(在项目属性→生成→平台目标中选择x64),能大幅提升可用内存空间,缓解这类问题。
可选小技巧:格式化JSON后再尝试
虽然作用有限,但单行JSON格式化(换行、缩进)后,解析过程中临时对象的创建可能更平稳。如果能临时成功解析一次,你可以先把JSON格式化到临时文件再处理:
// 先格式化JSON到临时文件 using (var sw = new StreamWriter("formatted_temp.json")) using (var jw = new JsonTextWriter(sw) { Formatting = Formatting.Indented }) { JsonSerializer.CreateDefault().Serialize(jw, JObject.Parse(File.ReadAllText(pathToFile))); } // 再解析格式化后的文件 var formattedJson = ReadJsonFile("formatted_temp.json");
内容的提问来源于stack exchange,提问作者kurkey




