You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

Newtonsoft.Json反序列化时偶发内存溢出问题求助

解决单行大JSON反序列化时的OutOfMemory异常

这个问题我之前帮同事排查过类似的情况,咱们来拆解下原因和可行的解决方案:

为什么会出现这个问题?

虽然你的JSON文件只有20MB,但**JObject会把整个JSON结构完整加载到内存中**,而单行大JSON的解析过程会产生大量临时对象,再加上JTokenJObject的基础单元)本身的对象开销(每个字段、值都是独立的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

火山引擎 最新活动