一尘不染

Json.NET缓存类型的序列化信息吗?

c#

在.NET世界中,涉及对象序列化时,通常会在运行时检查对象的字段和属性。通常,使用反射进行此工作很慢,并且在处理大量对象时是不可取的。另一种方法是使用IL发射或构建表达式树,这些树比反射提供了显着的性能提升。后者是大多数现代库在处理序列化时选择的。但是,在运行时建立和发布IL会花费时间,并且仅当此信息被缓存并重新用于相同类型的对象时,才可收回投资。

使用Json.NET时,对我来说不清楚使用哪种方法,如果确实使用了后者,则是否使用缓存。

例如,当我这样做时:

JsonConvert.SerializeObject(new Foo { value = 1 });

Json.NET是否构建Foo的成员访问信息并缓存以供日后重用?


阅读 247

收藏
2020-05-19

共1个答案

一尘不染

是的,它确实。
Json.NET在其IContractResolverDefaultContractResolver和中缓存类型序列化信息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的性能提示:重用合同解析器

2020-05-19