【问题标题】:Poor JAXB performance on "generic" use case - better design pattern?“通用”用例的 JAXB 性能不佳 - 更好的设计模式?
【发布时间】:2012-04-02 02:27:09
【问题描述】:

我尝试将一些基于 Apache digester 的 XML 序列化代码迁移到 JAXB,但结果很差。对于这种设计模式的最佳实践是否有一些建议(或放弃使用这个设计模式的 JAXB...)?

XML 在很大程度上依赖于接口和反射声明。

<someContainer>

  <someChild class="foo.Class1">
  </someChild>

</someContainer>

我使用带有此解组代码的适配器解决了间接问题

public ResultType unmarshal(Object object) throws Exception {
    Element element = (Element) object;
    String classname = element.getAttribute("class");
    Class clazz = Class.forName(classname);
    JAXBContext jc = getContext(clazz);
    Unmarshaller unmarshaller = jc.createUnmarshaller();
    Object result = unmarshaller.unmarshal(element, clazz);
    if (result instanceof JAXBElement) {
        return (ResultType) ((JAXBElement) result).getValue();
    }
    return (ResultType) result;
}

好吧,即使使用 JAXBContext 缓存,这到目前为止也优于基于消化器的代码(在这一点上没有确切的衡量标准,可以说大约 50 到 100 次)。实际性能损失的主要原因似乎在解组的“元素”参数中。大部分时间都花在创建一个新的 DocumentBuilder 环境,需要重新转换已经解析的元素

有什么建议吗?

【问题讨论】:

  • “远远超出范围”是什么意思?你指的是哪个失败?
  • 这表示性能很差。虽然原始摘要代码在大型文档上具有 2 到 4 秒的延迟,但上面的 JAXB 代码更多地位于分钟区域。什么是“失败”(从我的代码在此上下文中不可用的角度来看)是对元素参数的处理,它将再次被视为 DOM 源并由新的 DocumentBuilder 处理。乍一看,这似乎是花费最多时间的地方。

标签: java performance jaxb


【解决方案1】:

我做了很多这样的事情,不能抱怨 JAXB 性能差,这在我的案例中是最重要的。在一个相当慢的服务器上,同样的任务对于 5kb 的有效负载不超过 3-4 毫秒。至于缓存,我通常为您的案例采用以下双向数据结构来编组/解组:

  Map<String, MarshallData> marshalCache;

  public ResultType unmarshal(Object object) throws Exception {
    Element element = (Element) object;
    MarshallData md = marshalCache.get(element.getAttribute("class"));
    Object result = md.unmarshaller.unmarshal(element);
    if (result instanceof JAXBElement) {
        return (ResultType) ((JAXBElement) result).getValue();
    }
    return (ResultType) result;
  }

  public void registerMarshallData(Class clazz) throws Exception {
    JAXBContext jbc =  JAXBContext.newInstance(clazz); // or get it somewhere else if needed
    MarshallData mdata = new MarshallData(jbc.createMarshaller(), jbc.createUnmarshaller());
    marshalCache.put(clazz.getName(), mdata);
  }  

  class MarshallData {
    private Unmarshaller unmarshaller;
    private Marshaller marshaller;

    protected MarshallData(Marshaller marshaller, Unmarshaller unmarshaller) {
      this.marshaller = marshaller;
      this.unmarshaller = unmarshaller;
    }
  }

【讨论】:

  • 你有没有使用 JAXB 的参考not?至少对于 JDK 1.6 内置库“unmarshal(element)”将导致构建一个新的 DocumentBuilder,这反过来似乎是估计 50 倍性能损失的原因。
  • @mtraut,我没有这样的参考资料。当我遇到类似问题时,我进行了小型调查,并从处理有效负载的过程中删除了所有耗时的操作。例如,您实际上不需要为每个请求创建提到的DocumentBuilder,可以使用多个静态预构建文档构建器。此外,如果您使用大型文档,您可能会考虑使用 SAX 或尝试使用非默认 DOM 实现。
  • 我试了一下你的 sn-p 并将解组器本身添加到缓存中 - 没有成功。当您说使用 SAX 或非默认 DOM 时:您的意思是放弃 JAXB(问题究竟是什么,JAXB 是否适合这种用例)
  • @mtraut,不,我的意思是 JAXB 能够在 m/um 中使用不同的源和目标:io 流、dom、sax 源、xml 流等(有关详细信息,请参阅 API)之一在不放弃 JAXB 的情况下,它们可能会更好地满足您的需求。例如,众所周知,大型文档最好由org.xml.sax.* 处理而不是 DOM。顺便说一句,您是否检查过您没有为每个请求创建DocumentBuilder?挺贵的。
  • 查看接受的答案。我不会根据请求直接创建“DocumentBuilder”。这是在内部完成以处理 JAXB 中的 Element - 所以您的代码 sn-p 也是如此。你是对的,因为DocumentBuildeFactory 查找是降级的原因。添加启动参数可以解决此问题。我会在解锁时奖励赏金。顺便提一句。你认为处理这种情况的“规范”代码是什么?
【解决方案2】:

好吧,我添加这个以供其他想知道他们是否需要放弃 JAXB 的人参考。

添加

-Djavax.xml.parsers.DocumentBuilderFactory=com.sun.org.apache.xerces.internal.jaxp.DocumentBuilderFactoryImpl

完成了这项工作。现在的性能具有相同的数量级。看来我们有一个简单的类路径问题。在 large 类路径上,(未缓存的)DocumentBuilderFactory 所需的服务提供者查找是导致性能不佳的关键因素。给虚拟机一个已知的实现以避免搜索....

我在“随机”分析之后尝试过这个(随时按暂停 :-) 总是以这样的搜索顺序结束:

Thread [main] (Suspended)   
    owns: VirtualKeyStoreHolder  (id=417)   
    owns: CertificateStoreEnvironment  (id=418) 
    owns: ManagedCertificateProvider  (id=419)  
    owns: CertificateStoreEnvironment$1  (id=420)   
    WinNTFileSystem.getBooleanAttributes(File) line: not available [native method]  
    File.exists() line: 733 
    URLClassPath$FileLoader.getResource(String, boolean) line: 999  
    URLClassPath$FileLoader.findResource(String, boolean) line: 966 
    URLClassPath.findResource(String, boolean) line: 146    
    URLClassLoader$2.run() line: 385    
    AccessController.doPrivileged(PrivilegedAction<T>, AccessControlContext) line: not available [native method]    
    Launcher$AppClassLoader(URLClassLoader).findResource(String) line: 382  
    Launcher$AppClassLoader(ClassLoader).getResource(String) line: 1003 
    Launcher$AppClassLoader(ClassLoader).getResourceAsStream(String) line: 1193 
    SecuritySupport$4.run() line: 96    
    AccessController.doPrivileged(PrivilegedAction<T>) line: not available [native method]  
    SecuritySupport.getResourceAsStream(ClassLoader, String) line: 89   
    FactoryFinder.findJarServiceProvider(String) line: 250  
    FactoryFinder.find(String, String) line: 223    
    DocumentBuilderFactory.newInstance() line: 123  
    TransformerIdentityImpl.createResultContentHandler(Result) line: 215    
    TransformerIdentityImpl.setDocumentLocator(Locator) line: 881   
    DomLoader$State.<init>(DomLoader, UnmarshallingContext) line: 78    
    DomLoader<ResultT>.startElement(UnmarshallingContext$State, TagName) line: 113  
    XsiTypeLoader.startElement(UnmarshallingContext$State, TagName) line: 76    
    UnmarshallingContext._startElement(TagName) line: 481   
    UnmarshallingContext.startElement(TagName) line: 459    
    SAXConnector.startElement(String, String, String, Attributes) line: 148 
    SAXParserImpl$JAXPSAXParser(AbstractSAXParser).startElement(QName, XMLAttributes, Augmentations) line: 501  
    XMLNSDocumentScannerImpl.scanStartElement() line: 400   
    XMLNSDocumentScannerImpl$NSContentDriver(XMLDocumentFragmentScannerImpl$FragmentContentDriver).next() line: 2755    
    XMLNSDocumentScannerImpl(XMLDocumentScannerImpl).next() line: 648   
    XMLNSDocumentScannerImpl.next() line: 140   
    XMLNSDocumentScannerImpl(XMLDocumentFragmentScannerImpl).scanDocument(boolean) line: 511    
    XIncludeAwareParserConfiguration(XML11Configuration).parse(boolean) line: 808   
    XIncludeAwareParserConfiguration(XML11Configuration).parse(XMLInputSource) line: 737    
    SAXParserImpl$JAXPSAXParser(XMLParser).parse(XMLInputSource) line: 119  
    SAXParserImpl$JAXPSAXParser(AbstractSAXParser).parse(InputSource) line: 1205    
    SAXParserImpl$JAXPSAXParser.parse(InputSource) line: 522    
    UnmarshallerImpl.unmarshal0(XMLReader, InputSource, JaxBeanInfo) line: 211  
    UnmarshallerImpl.unmarshal(XMLReader, InputSource) line: 184    
    UnmarshallerImpl(AbstractUnmarshallerImpl).unmarshal(InputSource) line: 137 
    UnmarshallerImpl(AbstractUnmarshallerImpl).unmarshal(InputStream) line: 184 
    VirtualKeyStoreTools.createVirtualKeyStore(InputStream) line: 54    
... more to come

由于有悬而未决的赏金,我将把它提供给@Osw(当可以应用赏金时),因为答案“是的,你可以 JAXB”是正确的。我认为微小的性能差异并不能证明使用计划 SAX 解决方案或不再使用蒸煮器是合理的。此外,缓存 JAXBContext 似乎是明智的,我不知道缓存 unmarshaller 上下文 - 也许有人添加了有关此的信息。

感谢您的支持。

编辑

根据@Osw 的要求,这里有一些(令人失望的)数据,以反映由此产生的整体性能。

我直接围绕解析为新旧应用程序做了一些肮脏的检测。虽然对于加载一堆文件的交互式应用程序而言,这些数字是“可接受的”,但我必须承认消化器的速度仍然超过 2 倍。

  • 基于摘要器,约 1 MB 文件,包含 4000 个通用条目 = 400 毫秒
  • 基于 JAXB,见上文,900 毫秒

JAXB 实现已经包含一个用于解组器的缓存,因此完成了“开箱即用”的优化。这给我留下了一个迫切需要更多分析的功能应用程序。完成后我会回来,并且会弹出一些普遍感兴趣的有趣技巧。

【讨论】:

  • 感谢调查,添加到收藏夹真是令人惊讶。您能否在修复后发布分析数据?
  • 2x 差距在我看来更容易解释:SAX 与 DOM 加单一用途解决方案与 java 范围的技术。假设您的代码没有瓶颈,我仍然相信通过使用SAXStAX xml-streaming 或测试不同的xml 解析器可以在一定程度上缩小差距。第一个需要认真的代码重写,但机会更好。是否值得由您决定。
  • 我正在搜索/缺少的是一个(官方)钩子,它允许在通用元素开始时在解组过程中“注入”Java 类,而不是创建一个需要再次被解析。这应该会带来更好的性能和恕我直言更清洁的代码。一些极客做到了?
  • 这里有一些有趣的答案,展示了如何在将 xml 流事件传递给解组器之前捕获它们:stackoverflow.com/questions/277502/…
猜你喜欢
  • 1970-01-01
  • 2015-01-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-01-07
  • 1970-01-01
相关资源
最近更新 更多