Poor JAXB performance on "generic" use case - better design?

I've tried porting some Apache based XML serialization code to JAXB with very poor results. Is there any advice for best practice with this design pattern (or for ditching JAXB with this ...)?

XML relies primarily on interfaces and reflective declarations.

<someContainer>

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

</someContainer>

      

I have allowed indirect use with this non-marching code

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;
}

      

Well, even with JAXBContext caching, this is far superior to digest based code (without an exact answer to this question, say 50 to 100 times). The main cause of the actual performance loss appears to be the item parameter for the unmarshal. Most of the time is spent creating a new DocumentBuilder to re-transform an already parsed element

Any advice?

+3


source to share


2 answers


Well, I'll add this for reference to other people interested in the need to ditch JAXB.

Adding

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

      

did this job. The performance is now in the same order of magnitude. We seem to have a simple classpath problem. In the case of a large path class, finding the service provider required for the (uncached) DocumentBuilderFactory was a key contributor to poor performance. Give the VM a known implementation to avoid searching ....

I tried this after "stochastic" profiling (the press pauses whenever you like :-) always hit the search sequence, for example:

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

      

As there is pending bounty, I will suggest this @Osw (when the bounty can be served) as a "Yes, you can JAXB" answer correctly. I think that minor performance differences do not justify using a SAX solution plan or no longer work with a digester. Also, JAXBContext caching seems wise, I can't say about unmarshaller context caching - maybe someone is adding information about this.



Thanks for your support.

EDIT

As requested by @Osw, here are some (disappointing) numbers for overall performance.

I've made some dirty tools for the old and new app directly around parsing. While the numbers are "acceptable" for an interactive application downloading a bunch of files, I have to admit that the digester is still more than 2 * faster.

  • Digester-based file ~ 1 MB containing 4000 total records = 400ms
  • JAXB, see above, 900 ms

The JAXB implementation already contains a cache for the unmarshaller, so an optimization is done out of the box. This leaves me with a functional application that urgently needs additional profiling. I'll be back when it's done and some interesting tricks of general interest come up.

+3


source


I do a lot of things like this and cannot complain about the poor JAXB performance which is of utmost importance in my case. The exact same task takes no more than 3-4ms for a 5k payload on a rather slow server. As far as caching is concerned, I usually create the following bi-directional data structure for marshaling / unmarshalling, which is accepted for your case:



  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;
    }
  }

      

+4


source







All Articles