Performance 禁用Jaxb2中的字符串暂存可加快从FastInfo流中解组的速度。为什么禁用它如此困难?

Performance 禁用Jaxb2中的字符串暂存可加快从FastInfo流中解组的速度。为什么禁用它如此困难?,performance,jaxb,unmarshalling,jaxb2,fastinfoset,Performance,Jaxb,Unmarshalling,Jaxb2,Fastinfoset,我已经测量了使用Jaxb2将XML解组到对象所需的时间成本,该Jaxb2使用了一个大的(1.7mb)XML负载和一些长的(48个字符)标记名。我通过在采样模式下运行的JProfiler观察到,字符串实习工作是所花费时间的一部分 我做了一些研究,发现Jaxb可以在不插入字符串的模式下运行。我的理论是,在某些情况下,在解组过程中不插入字符串可以提高性能,但代价是使用更多堆内存,因为在插入过程中不必对每个标记名字符串进行散列 我用来抑制Jaxb的内部行为的方法是在FastInfo集“StAXDocum

我已经测量了使用Jaxb2将XML解组到对象所需的时间成本,该Jaxb2使用了一个大的(1.7mb)XML负载和一些长的(48个字符)标记名。我通过在采样模式下运行的JProfiler观察到,字符串实习工作是所花费时间的一部分

我做了一些研究,发现Jaxb可以在不插入字符串的模式下运行。我的理论是,在某些情况下,在解组过程中不插入字符串可以提高性能,但代价是使用更多堆内存,因为在插入过程中不必对每个标记名字符串进行散列

我用来抑制Jaxb的内部行为的方法是在FastInfo集“StAXDocumentParser”(实现XMLStreamReader)上设置“org.codehaus.stax2.internNames”和“org.codehaus.stax2.internNsUris”属性。我不完全清楚为什么必须将这些设置为“true”以防止Jaxb插入字符串,但这就是它的工作原理

这些JUnit驱动的测试就是我用来总结禁用Jaxb的字符串插入行为会带来很大的性能差异的原因:

所以我的问题是多方面的:

1) 我是否误解了一些重要的事情,不应该首先尝试禁用Jaxb的字符串实习行为

2) 有没有更好的方法来指导Jaxb不实习字符串?“StAXManager”类不允许您设置这些面向Woodstox的属性。对于这个测试,我最终扩展了StAXManager,如下所示,以解决这个问题。这是一种我不愿意在生产中使用的黑客技术。我怀疑这里的想法是,当Jaxb从Woodstox流中解包时,它会查看Woodstox是否已经在进行实习,以及当“是”时Jaxb会通过禁用该流程步骤做出反应。我是通过在Jaxb库中使用这种逻辑来作弊的,所以我希望有更好的方法来实现这一点

package com.sun.xml.fastinfoset.stax;
public class JaxbStringInternSuppressionStaxManager extends StAXManager {
    public JaxbStringInternSuppressionStaxManager() {
        // Add to the allowable list of feature names so that the user may set these "StAXInputFactory" properties
        super.features.put("org.codehaus.stax2.internNames", null);
        super.features.put("org.codehaus.stax2.internNsUris", null);
    }
}
更新:

和往常一样,“一个好的问题是一半答案”。我刚才在起草这个问题时注意到,“com.sun.xml.internal.bind.v2.runtime.unmarshaller.StAXStreamConnector”检查类“com.sun.xml.internal.fastinfoset.stax.StAXDocumentParser”是否可以从您正在使用的XMLStreamReader中分配,如果是,则不启用字符串内部。在我的例子中,我的流对象是“com.sun.xml.fastinfoset.stax.StAXDocumentParser”,因此不会禁用interning。现在的问题是“为什么它只针对FastInfo库的内部风格这样做?”也许我会通过仔细阅读找到答案


另外,如果有一个更好的论坛来讨论这类问题,比如一个活跃的开发者用户组,请分享这些信息,我会把他们链接到这篇文章,这样合适的人就会看到这个问题。

我不一定相信一个剖析器或一个测试,如果不考虑实习和不实习的真实用例,所以要有点怀疑。然而,实习生也有一些问题。特别是,它使用固定大小的池大小,因此当池满时,哈希查找的可能恒定性能降低为搜索链接列表。请参阅以获得更详细的讨论

简言之,您可以尝试使用
-XX:StringTableSize=n
(其中n最好是prime)更改池大小,并查看发生了什么

使用
-XX:+PrintStringTableStatistics
查看程序终止时池的使用情况,并尝试不同的大小


编辑:这是一个试图回答“有更好的方法吗”(即让实习生更快)。我将把另一个问题留给更有资格的人解决。

解决方案选项1:将整个应用程序切换到不同的jaxb实现的简单方法

拉入jaxb impl以使用一个版本的jaxb,该版本在FastInfo库中性能更好:

<!-- Both of these libs must be here in order to get performant behavior out of Jaxb by default.
-->
<dependency>
        <groupId>com.sun.xml.fastinfoset</groupId>
        <artifactId>FastInfoset</artifactId>
        <version>1.2.13</version>
        <scope>compile</scope>
</dependency>
<dependency> <!-- This artifactId also exists under javax.xml.bind but it appears that nobody uses that one... -->
    <groupId>javax.xml</groupId>
    <artifactId>jaxb-impl</artifactId>
    <version>2.1</version>
    <scope>runtime</scope>
</dependency>
<!-- End: Both of these libs... -->

com.sun.xml.fastinfoset
快速信息集
1.2.13
编译
javax.xml
jaxb impl
2.1
运行时
这将产生更新其余代码所使用的jaxb版本的副作用。在某些情况下可能不可取。例如,如果您正在创建一个需要在各种应用程序中使用的共享库,那么在他们拉入您的共享组件时更改此功能是不礼貌的

解决方案选项2:使用JVM的jaxb实现和性能攻击欺骗它,使其相信字符串已经被插入(实现起来更复杂)

  • 使用“maven shade插件”对FastInfo库的类进行着色和重新打包。结果应该是一个无逻辑的maven组件。这是可选的,旨在确保使用FastInfo编解码器组件的用户不会因为编解码器库拉入的可传递依赖项而发生类路径冲突
  • 创建一个my FastInfo编解码器库,该库提供一个简单的API来编码和解码FastInfo有效负载(考虑使用InputStreams和OutputStreams作为参数,使用XMLStreamReader作为解码器的返回类型)。在重新打包的FastInfo库上添加依赖项。请注意,如果您使用Eclipse,当启用m2e的“工作区分辨率”时,它不能很好地处理着色库,所以请在您的编解码器项目中禁用它
  • 在我的FastInfo编解码器中添加一个类,该类扩展重新打包的FastInfo库的“StAXManager”。此类应该有助于设置属性,这些属性告诉jaxb所提供的XMLStreamReader已经插入了NS和标记名字符串。示例如下:
包myrepackagedfastinfosetclassespackageprefix.shade.com.sun.xml.fastinfoset.stax; 导入myrepackagedfastinfosetclassespackageprefix.shade.com.sun.xml.fastinfoset.stax.StatxManager; 公共类JaxBStringInterpressionsTaxManager扩展了StatxManager{ 公共JAXbstringInterpressionsTaxManager(){ package myrepackagedfastinfosetclassespackageprefix.shaded.com.sun.xml.fastinfoset.stax; import myrepackagedfastinfosetclassespackageprefix.shaded.com.sun.xml.fastinfoset.stax.StAXManager; public class JaxbStringInternSuppressionStaxManager extends StAXManager { public JaxbStringInternSuppressionStaxManager() { // Add to the allowable list of feature names so that the user may set these "StAXInputFactory" properties super.features.put("org.codehaus.stax2.internNames", null); super.features.put("org.codehaus.stax2.internNsUris", null); } /** * This is an optimization. The FastInfoset libraries already intern strings and the JVM's jaxb implementation by default * unnecessarily repeats that work. This is true at least for the 64 bit version of jdk1.8.0_121. * * The way that this workaround works is by piggybacking on a Jaxb optimization for the Woodstox parser. When we set * these properties it tells jaxb that Woodstox has already interned the strings which causes it to disable its * string interning. * * We did explore the cleaner option of pulling in the Maven "javax.xml:jaxb-impl" artifact as a dependency instead of using * the JVM's jaxb library. That external jaxb library when used with the FastInfoset library does perform substantially better * than the JVM's but isn't 100% as fast as the JVM's with interning disabled. The key reason that we quit exploring that solution * is that when you repackage (via maven-shade-plugin) the jaxb libraries they no longer work with our standard jaxb binding * maven components due to statements like "if ( instanceof my_repackaging_project.shaded.XMLElement)" * used during the data mapping process. */ public JaxbStringInternSuppressionStaxManager enableTrickToStopJaxbFromInterningStrings() { super.setProperty("org.codehaus.stax2.internNames", true); super.setProperty("org.codehaus.stax2.internNsUris", true); return this; } }