JSchemaValidatingReader处理大字符串令牌时内存溢出问题求助
解决Newtonsoft.Json.Schema验证大字符串时的内存占用问题
问题概述
使用Newtonsoft.Json v13.0.3、Newtonsoft.Json.Schema 4.0.1和.NET 9.0.304验证JSON时,当传入含10M字符的demoId字段时,验证错误消息会包含完整的大字符串,经dotMemory检测该字符串在内存中存在4份,占用约80MB内存。需要在不自定义JsonTextReader或核心验证逻辑的前提下优化内存占用。
解决方案
1. 自定义Schema错误消息模板,避免嵌入大字符串
修改JSON Schema,为maxLength规则指定不包含实际字段值的错误提示,替代默认会嵌入完整字符串的消息:
{ "type": "object", "additionalProperties": false, "required": [ "demoId" ], "properties": { "demoId": { "type": "string", "maxLength": 50, "errorMessage": "字段 demoId 长度超过最大限制(50字符)" } } }
2. 优化ValidationEventHandler,避免引用大字符串实例
在事件处理逻辑中,不要直接使用默认的a.Message(该消息会包含完整的大字符串),而是通过ValidationError的路径、规则等属性构造轻量错误消息,同时标记错误已处理,避免后续默认逻辑持有大字符串引用:
var validationResult = new Lazy<ReplyErrorReqV1Payload>(); var streamReader = new StreamReader(stream); var jsonReader = new JsonTextReader(streamReader); var validatingReader = new JSchemaValidatingReader(jsonReader); validatingReader.Schema = schema; bool validationFailed = false; validatingReader.ValidationEventHandler += (o, a) => { // 构造不包含大字符串的错误消息 var errorMsg = $"验证失败:路径[{a.ValidationError.Path}] 违反{a.ValidationError.Rule}规则,{a.ValidationError.Message}"; Console.WriteLine(errorMsg); validationFailed = true; a.Handled = true; // 标记错误已处理,阻止默认消息生成逻辑 }; // 一旦检测到错误就停止读取,避免加载剩余大字符串 while (!validationFailed && await validatingReader.ReadAsync(cancellationToken)) { // 仅驱动读取到错误发生时为止 }
3. 额外优化:提前终止读取流程
当验证错误触发后,立即终止JSON读取流程,避免继续加载剩余的大字符串内容,进一步减少内存占用。上述代码中已经通过validationFailed标记实现了这一点。
原理说明
- 默认的验证错误消息会将违规字段的完整值嵌入其中,导致大字符串被多次引用(事件参数、消息字符串等),无法被GC及时回收。
- 通过自定义Schema错误消息和轻量事件处理逻辑,彻底避免对大字符串实例的引用,同时提前终止读取,减少不必要的内存加载。
内容的提问来源于stack exchange,提问作者Libor Svoboda
相关产品推荐
相关产品推荐

