Json.NET缓存类型';序列化信息
本文关键字:序列化 信息 NET 缓存 类型 Json | 更新日期: 2023-09-27 18:20:41
在.NET世界中,当涉及到对象序列化时,通常会在运行时检查对象的字段和属性。对此作业使用反射通常很慢,并且在处理大型对象集时是不可取的。另一种方法是使用IL发射或构建表达式树,这些表达式树在反射方面提供了显著的性能增益。后者是大多数现代库在处理序列化时选择的。然而,在运行时构建和发出IL需要时间,并且只有缓存这些信息并将其重新用于相同类型的对象时,投资才会得到回报。
在使用Json.NET时,我不清楚使用了上面描述的哪种方法,如果确实使用了后者,是否使用了缓存。
例如,当我这样做时:
JsonConvert.SerializeObject(new Foo { value = 1 });
Json.NET是否构建Foo的成员访问信息和缓存以便以后重用?
是的Json.NET在其IContractResolver
类DefaultContractResolver
和CamelCasePropertyNamesContractResolver
中缓存类型序列化信息。除非指定自定义协定冲突解决程序,否则会缓存并重用这些信息。
对于DefaultContractResolver
,在内部维护一个全局静态实例,每当应用程序没有指定自己的协定解析程序时,Json.NET就会使用该实例。另一方面,CamelCasePropertyNamesContractResolver
维护在所有实例之间共享的静态表。(我认为这种不一致性源于遗留问题;请参阅此处了解详细信息。)
这两种类型都被设计为完全线程安全的,因此线程之间的共享应该不会成为问题。
如果您选择实现和实例化自己的契约解析程序,那么只有在缓存和重用契约解析程序实例本身的情况下,类型信息才会被缓存和重用。因此,Newtonsoft建议:
为了提高性能,您应该创建一次契约解析程序,并在可能的情况下重用实例。解析合约很慢,IContractResolver的实现通常会缓存合约。
如果内存消耗是一个问题并且无论出于何种原因,您都需要最小化缓存合约永久占用的内存,您可以构建自己的DefaultContractResolver
本地实例(或某些自定义子类),使用该实例进行序列化,然后立即删除对它的所有引用,例如:
public class JsonExtensions
{
public static string SerializeObjectNoCache<T>(T obj, JsonSerializerSettings settings = null)
{
settings = settings ?? new JsonSerializerSettings();
bool reset = (settings.ContractResolver == null);
if (reset)
// To reduce memory footprint, do not cache contract information in the global contract resolver.
settings.ContractResolver = new DefaultContractResolver();
try
{
return JsonConvert.SerializeObject(obj, settings);
}
finally
{
if (reset)
settings.ContractResolver = null;
}
}
}
如果您正在使用CamelCasePropertyNamesContractResolver
,请使用适当的命名策略切换到DefaultContractResolver
,例如:
settings.ContractResolver = new DefaultContractResolver { NamingStrategy = new CamelCaseNamingStrategy() };
缓存的大部分约定内存(但不是全部)最终将被垃圾回收。当然,这样做,序列化性能可能会受到严重影响。(一些包含反映的信息的表,例如enum
类型和数据契约属性是全局共享的,而不是回收的。)
有关更多信息请参阅Newtonsoft的性能提示:重用合同解析程序。