反序列化深度过大导致假死而非长度超限,表现为线程卡死、CPU飙升、无异常日志;需区分maxJsonLength超限(抛InvalidOperationException)与深度问题(栈溢出、GC频繁);检查隐式循环引用、配置MaxDepth限制、用线程快照定位递归卡点。

这类问题本质是反序列化过程陷入无限递归或层级过深,而非单纯长度超限。它常表现为线程卡死、CPU飙升、响应无返回,日志里却可能没有明显异常——因为还没走到抛错那步,程序已“假死”。
确认是否真为深度过大而非长度超限
先区分两个常见但不同的问题:
-
maxJsonLength 超限:会明确抛出
InvalidOperationException,提示“字符串的长度超过了为 maxJsonLength 属性设置的值”,属于 JSON 文本字面量太长; -
反序列化深度过大:不报长度错,而是线程长时间无响应、GC 频繁、堆内存缓慢上涨,甚至触发 JVM 线程栈溢出(
StackOverflowError),根源是对象图嵌套层级失控(比如循环引用未处理、树形结构无深度限制、DTO 误含自身引用)。
检查对象模型是否存在隐式循环引用
这是最常见诱因。例如一个 User 类包含 Department,而 Department 又反向持有 User 列表,且序列化器未配置引用跟踪(如 ReferenceLoopHandling.Ignore):
- 用调试器在反序列化前观察传入 JSON,搜索
"$id"、"$ref"字段(若用了 Newtonsoft.Json 的默认设置); - 在类定义中检查是否有双向导航属性(如
public virtual ICollection<User> Users { get; set; }+public virtual Department Dept { get; set; }); - 临时移除疑似关联属性,看问题是否消失——可快速定位根因。
验证反序列化器是否启用了深度保护
不同序列化器策略不同,需针对性检查:
-
Newtonsoft.Json:检查是否设置了
MaxDepth(默认为 0,即不限制)。应在JsonSerializerSettings中显式设值,例如new JsonSerializerSettings { MaxDepth = 16 }; -
System.Text.Json(.NET Core 3.0+):默认深度限制为 64,可通过
JsonSerializerOptions.MaxDepth调整,但更推荐从模型设计上规避深层嵌套; -
JavaScriptSerializer(已弃用):本身不提供深度控制,仅支持
MaxJsonLength,遇到深度问题基本只能重构模型或换序列化器。
用最小复现 + 线程快照定位卡点
当现象是“假死”,不要只看日志:
- 在测试环境复现时,用
dotnet-dump collect或jstack(Java)抓取线程堆栈,重点看正在执行JsonSerializerInternalReader或ReadAsInt32等递归方法的线程; - 观察调用栈是否出现数十层重复的
PopulateObject、CreateObject或Deserialize—— 这是深度失控的典型痕迹; - 配合内存快照(如 dotnet-gcdump)查看是否有大量未释放的临时对象,佐证递归未正常退出。
不复杂但容易忽略:深度问题往往藏在业务无关的通用 DTO 或 ORM 实体中,排查时别只盯着当前接口的入参类型,要顺藤摸瓜查它所引用的所有子类型。

















