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

使用Json.NET时,不清楚我可以使用上述哪种方法,如果确实使用了后者,则是否使用缓存。例如,当我这样做时:

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


Json.NET是否可以构建Foo的成员访问信息并缓存以备将来使用?

评论

我没有确切的答案,但是Json.NET的源代码位于github上,它确实说“ Json.NET是.NET的流行高性能JSON框架”。如果您在源上快速搜索缓存,则会发现确实存在大量缓存。

#1 楼

是的,它确实。 Json.NET在其IContractResolverDefaultContractResolverCamelCasePropertyNamesContractResolver内部缓存类型序列化信息。除非指定自定义合同解析器,否则此信息将被缓存和重用。对于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,例如:

settings.ContractResolver = new DefaultContractResolver { NamingStrategy = new CamelCaseNamingStrategy() };


大部分缓存的协定内存(但不是全部)最终都会被垃圾回收。当然,这样做会严重影响序列化性能。 (某些包含有关DefaultContractResolver类型和数据协定属性的反映信息的表是全局共享的,不会被回收。)

有关更多信息,请参阅Newtonsoft的性能提示:重用合同解析器。

评论


我们在所有UT中看到保存在MyContractResolver:CamelCasePropertyNamesContractResolver类型中的合同是静态缓存的,即使跨多个MyContractResolver类型也是如此。当一些UT一起运行时,这会导致令人惊讶的混乱,因为它们没有根据需要设置合同。重用同一ContractResolver实例的指针仍然有效吗?

– ElFik
4月1日1:26

@ElFik-CamelCasePropertyNamesContractResolver的行为与DefaultContractResolver不同-不管是否需要,它都会全局缓存合同信息。如果您不想这样做,请使用适当的命名策略切换到DefaultContractResolver。

–dbc
5月5日19:54