JAXB在“通用”用例上表现不佳 - 更好的设计模式?

时间:2012-03-16 14:25:28

标签: java performance jaxb

我尝试将一些基于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次)。实际性能损失的主要原因似乎是unmarshal的“元素”参数。大部分时间用于创建重新转换已经解析的元素所需的新DocumentBuilder环境

有什么建议吗?

2 个答案:

答案 0 :(得分:4)

我做了很多像这样的事情并且不能抱怨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;
    }
  }

答案 1 :(得分:3)

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

添加

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

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

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

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 *。

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

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