System.Text.Json 序列化吞吐量明显更高,但默认不支持循环引用和某些类型
在 Web API 或微服务高频请求场景下,
System.Text.Json的序列化/反序列化吞吐量通常比
Newtonsoft.Json高 1.5–2.5 倍,尤其在小对象(如 DTO)密集读写时。这源于它基于
Span<byte></byte>和零分配设计,避免了大量字符串拼接与中间
StringBuilder缓冲区。但它的默认行为更“严格”:不处理对象图中的循环引用、不支持
DataTable或
ExpandoObject、对
Dictionary<string object></string>的键类型要求更死板。
Newtonsoft.Json 在高并发下 GC 压力更大,尤其使用默认设置时
典型表现是
Gen 0GC 频率陡增,甚至触发
Gen 1,导致请求延迟毛刺。原因包括:
JToken层级结构堆分配多、默认启用
TypeNameHandling时写入额外元数据、
JsonConvert.SerializeObject每次调用都可能新建内部缓存上下文(除非显式复用
JsonSerializer实例)。实操建议: 始终复用
JsonSerializer实例(而非反复调用静态
JsonConvert方法) 禁用
TypeNameHandling,除非真需要反序列化为运行时未知类型 对已知结构的 JSON,优先用
JObject.Parse()+ 显式属性访问,而非
DeserializeObject<t>()</t>
高并发下 System.Text.Json
的坑:自定义转换器 + 同步锁易成瓶颈
当需要兼容旧系统(如日期格式为
"yyyy-MM-dd")、或处理
DBNull、
KeyValuePair等特殊值时,常需注册自定义
JsonConverter<t></t>。若转换器内部用了
lock、
Monitor.Enter或非线程安全的静态缓存(如
ConcurrentDictionary误用为普通
Dictionary),会直接拖垮吞吐。示例错误写法:
public class BadDateConverter : JsonConverter<DateTime>
{
private static readonly Dictionary<string, DateTime> _cache = new(); // 非线程安全!
public override DateTime Read(...){ ... }
public override void Write(...){ ... }
}
正确做法是:用
ConcurrentDictionary、避免锁、或把缓存逻辑移到上层(如 ASP.NET Core 的
HttpContext.Items)。
真实压测中,差异往往卡在配置而非底层库
很多团队测出“Newtonsoft 更快”,实际是因为没关
System.Text.Json的默认验证(如
PropertyNameCaseInsensitive = true开销显著)或没预热
JsonSerializerOptions。关键配置对比:
System.Text.Json:必须提前构造单例
JsonSerializerOptions,设
PropertyNameCaseInsensitive = false、禁用
WriteIndented、注册转换器用
options.Converters.Add(...)而非每次新建
Newtonsoft.Json:用
JsonSerializer.Create(settings)构建实例,禁用
Formatting.Indented、
ReferenceLoopHandling、
TypeNameHandling两者都应关闭日志/诊断(如 ASP.NET Core 中禁用
Microsoft.AspNetCore.Mvc.Formatters.JsonOutputFormatter的详细错误)
真正决定高并发表现的,经常是这些配置是否被正确固化,而不是选哪个库本身。
