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

Newtonsoft JsonSerializerSettings重复创建为何引发高CPU与JIT开销?

为什么每次创建JsonSerializerSettings会引发高CPU占用?

问题场景

通过实现SnakeCaseAttribute(继承自ActionFilterAttribute),让部分控制器返回snake_case格式的JSON。原代码在每次请求的OnActionExecuted方法中新建JsonSerializerSettings,导致CPU占用极高;改为静态实例后,性能提升10-15倍,JIT编译耗时从每秒约1000ms降至约10ms。

原代码(性能问题版本)

public class SnakeCaseAttribute : ActionFilterAttribute
{
    public override void OnActionExecuted(ActionExecutedContext context)
    {
        if (context.Result is ObjectResult objectResult)
        {
            objectResult.Formatters.Add(new NewtonsoftJsonOutputFormatter(
                new JsonSerializerSettings // 性能瓶颈所在
                {
                    ContractResolver = new DefaultContractResolver
                    {
                        NamingStrategy = new SnakeCaseNamingStrategy
                        {
                            OverrideSpecifiedNames = false
                        },
                    },
                },
                context.HttpContext.RequestServices.GetRequiredService<ArrayPool<char>>(),
                context.HttpContext.RequestServices.GetRequiredService<IOptions<MvcOptions>>().Value));
        }
        else
        {
            base.OnActionExecuted(context);
        }
    }
}

优化后代码

public class SnakeCaseAttribute : ActionFilterAttribute
{
    // 静态实例,全局复用
    private static readonly JsonSerializerSettings _snakeCaseSettings = new JsonSerializerSettings
    {
        ContractResolver = new DefaultContractResolver
        {
            NamingStrategy = new SnakeCaseNamingStrategy
            {
                OverrideSpecifiedNames = false
            },
        },
    };

    public override void OnActionExecuted(ActionExecutedContext context)
    {
        if (context.Result is ObjectResult objectResult)
        {
            objectResult.Formatters.Add(new NewtonsoftJsonOutputFormatter(
                _snakeCaseSettings, // 复用静态配置
                context.HttpContext.RequestServices.GetRequiredService<ArrayPool<char>>(),
                context.HttpContext.RequestServices.GetRequiredService<IOptions<MvcOptions>>().Value));
        }
        else
        {
            base.OnActionExecuted(context);
        }
    }
}

底层逻辑与性能提升原因

1. DefaultContractResolver的契约缓存机制

Newtonsoft.Json的DefaultContractResolver核心工作是为每个.NET类型生成序列化契约——包含属性名映射(比如驼峰转蛇形)、序列化规则、忽略项等元数据。为了避免重复计算,DefaultContractResolver会默认缓存这些契约。

但如果每次请求都新建DefaultContractResolver实例:

  • 缓存无法复用,每处理一个请求都要重新遍历目标类型的属性、执行字符串转换(驼峰→蛇形)、生成全新的契约
  • 这些操作涉及大量反射和字符串处理,是CPU消耗的主要来源

2. JsonSerializerSettings的重复初始化开销

每次新建JsonSerializerSettings时,除了实例化DefaultContractResolver,还要初始化一系列默认配置(比如日期格式、空值处理规则)、绑定内部事件等。单次开销虽小,但高并发场景下会累积成显著的CPU负载。

3. JIT编译的重复执行

CLR的JIT编译器会在方法首次执行时将IL代码编译为机器码。原代码中,每次新建JsonSerializerSettings和NewtonsoftJsonOutputFormatter,都会触发相关序列化方法的重复JIT编译;而静态实例只会在应用启动时编译一次,后续请求直接复用已编译的机器码,这就是JIT耗时大幅下降的关键。

4. 线程安全性说明

JsonSerializerSettings和DefaultContractResolver在初始化完成后是线程安全的(只要不动态修改配置),因此使用静态实例不会引发并发问题。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.01 05:36:00