当前位置: 首页 > 知识库问答 >
问题:

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

钮出野
2023-03-14

我已经测量了使用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的字符串驻留行为会带来很大的性能差异:

https://github.com/gjd6640/fastinfoset-performance-evaluation

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

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,所以实习不会被禁用。现在的问题是“为什么它这样做只是为了Fastinfoset库的内部风味?”也许我会仔细阅读这篇文章找到答案。

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

共有2个答案

吴修洁
2023-03-14

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

拉入jaxb-impl以使用使用此Fastinfoset库性能更好的Jaxb版本:

<!-- 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... -->

这样做的副作用是更新了其他代码所使用的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和标记名字符串。示例如下:
    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;
        }
    }

解决方案选项3:有足够多的人与Oracle签订了JVM支持合同,他们提出要求获得某种非内部快速信息集支持。

我希望对于Oracle来说,教JVM提供的jaxb实现从给定的XMLStreamReader确定这个FastInfo集实现配置为intern字符串是相当简单的。

没有成功的解决方案的可能性:从上面的解决方案1中重新打包两个罐子

可以使用“maven shade plugin”或类似工具创建带有自定义前缀包名的新JAR。经过一些修改后,这些库确实起了作用。然而,我得到的最终结果是,重新打包的jaxb库现在希望jaxb RI生成的OXM对象具有来自新的着色包名称的注释。我的解决方案是以标准方式构建的,因此我重新打包的解决方案不会将任何数据映射到我的对象。我不想强制要求我们的OXM绑定库使用重新打包的jaxb库,我也不太喜欢这种方法来更仔细地探索重新打包的方法,以免更改用于这些注释的包。

我没有探究的解决方案选项:

使用JVM的快速信息集类,这些类的包名中包含“内部”。这些可能在JVM附带的jaxb实现中表现良好,但我拒绝让“未来的我”面临使用内部apis带来的支持成本。

韦智刚
2023-03-14

我不一定信任分析器或测试,如果没有实习生和实习生也没有测量真正的用例,所以要有点怀疑。然而,实习生有一些问题。特别是,它使用固定大小的池大小,因此当池已满时,哈希查找的恒定性能会降低到搜索链表。见http://java-performance.info/string-intern-in-java-6-7-8/更长的讨论。

简而言之,您可以尝试使用-XX: StringTablesize=n(其中n应该理想地是质数)来更改池大小,然后看看会发生什么。

使用-XX: PrintStringTable统计查看当程序终止并尝试不同大小时池是如何使用的。

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

 类似资料:
  • 我想执行group_by,并使用dplyr对数据帧进行字符串操作 我希望输出(newdf)如下所示: 我在dplyr中尝试了以下方法 还尝试了以下方法: 我可以用基本的数据帧操作。在理解dplyr中的出路时需要帮助。

  • 问题内容: 我有一个名为Memcached.Js的项目,它是Memcached服务器到Node.js的端口。 我一直在使用字符串和缓冲区进行比较,比较内存占用量和性能。对于内存,毫无疑问,缓冲区是正确的选择。 但令我惊讶的是,表演并非如此。执行字符串操作比使用缓冲区更快。这是我尝试的: 完整的代码在这里:https : //github.com/dalssoft/memcached.js/blob

  • 我找不到一种方法来以编程方式禁用Android中的快速设置,这是我们企业启动器的要求。 除了如何在Android中禁用通知栏下拉菜单之外,还有什么线索吗?和https://e2e.ti.com/support/embedded/android/f/509/t/283260 有可能吗?谢谢

  • 本文向大家介绍JavaFX折线图中的符号是什么。如何禁用它们?,包括了JavaFX折线图中的符号是什么。如何禁用它们?的使用技巧和注意事项,需要的朋友参考一下 内联图数据值已表示由一条线连接的一系列点。在JavaFX中,您可以通过实例化javafx.scene.chart.LineChart类来创建折线图。 默认, JavaFX折线图包含沿x轴指出系列中的值的符号。通常,这些是小圆圈。 图底部的X

  • 我在输入流(来自键盘)中大约有行。我是这样读的: 是要读取的行数。是我在其他地方定义的。 这个方法正在做的工作,但我想加快它。我想过使用缓冲区将整个流读取到内存中并进行解析,但我不知道如何使用C++11来实现这一点。 我使用读取流,因为它似乎比C++流读取器快。否则,我更喜欢C++11解决方案。

  • 本文向大家介绍快速了解Node中的Stream流是什么,包括了快速了解Node中的Stream流是什么的使用技巧和注意事项,需要的朋友参考一下 Stream Buffer 的工作原理 Data 是一块大数据 他被分为很多个小数据 每块小数据都被存储在内存中的 Buffer 中 接着 Buffer 不断接收小数据 同时一旦 Buffer 接收的小数据填满了就会被消费 填满的 Buffer 也被称为一