2017-10-14 6 views
0

Je souhaite transformer le fichier XML en utilisant XSLT. je fait:OutOfMemoryError: espace de segment Java utilisant la transformation XSLT

TransformerFactory factory = TransformerFactory.newInstance(); 
    InputStream is = 
this.getClass().getResourceAsStream(getPathToXSLTFile()); 
    Source xslt = new StreamSource(is); 
    Transformer transformer = factory.newTransformer(xslt); 
    Source text = new StreamSource(new File(getInputFileName())); 
    transformer.transform(text, new StreamResult(new File(getOutputFileName()))); 

Quel fichier d'entrée ont sur 10000000 lignes, j'ai erreur:

Exception in thread "main" java.lang.OutOfMemoryError: Java heap space 
at com.sun.org.apache.xml.internal.utils.FastStringBuffer.append(FastStringBuffer.java:682) 
at com.sun.org.apache.xml.internal.dtm.ref.sax2dtm.SAX2DTM.characters(SAX2DTM.java:2111) 
at com.sun.org.apache.xalan.internal.xsltc.dom.SAXImpl.characters(SAXImpl.java:863) 
at com.sun.org.apache.xerces.internal.parsers.AbstractSAXParser.characters(AbstractSAXParser.java:546) 
at com.sun.org.apache.xerces.internal.impl.XMLDocumentFragmentScannerImpl.scanDocument(XMLDocumentFragmentScannerImpl.java:455) 
at com.sun.org.apache.xerces.internal.parsers.XML11Configuration.parse(XML11Configuration.java:841) 
at com.sun.org.apache.xerces.internal.parsers.XML11Configuration.parse(XML11Configuration.java:770) 
at com.sun.org.apache.xerces.internal.parsers.XMLParser.parse(XMLParser.java:141) 
at com.sun.org.apache.xerces.internal.parsers.AbstractSAXParser.parse(AbstractSAXParser.java:1213) 
at com.sun.org.apache.xalan.internal.xsltc.dom.XSLTCDTMManager.getDTM(XSLTCDTMManager.java:421) 
at com.sun.org.apache.xalan.internal.xsltc.dom.XSLTCDTMManager.getDTM(XSLTCDTMManager.java:215) 
at com.sun.org.apache.xalan.internal.xsltc.trax.TransformerImpl.getDOM(TransformerImpl.java:556) 
at com.sun.org.apache.xalan.internal.xsltc.trax.TransformerImpl.transform(TransformerImpl.java:739) 
at com.sun.org.apache.xalan.internal.xsltc.trax.TransformerImpl.transform(TransformerImpl.java:351) 
at ru.magnit.task.utils.AbstractXmlUtil.transformXML(AbstractXmlUtil.java:66) 
at ru.magnit.task.EntryPoint.main(EntryPoint.java:72) 

Dans cette ligne:

transformer.transform(text, new StreamResult(new File(getOutputFileName()))); 

Quelle est la raison pour cela et peut-elle être optimisé en quelque sorte, sans la taille du tas?

MISE À JOUR: Mon fichier XSLT:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> 

<xsl:output method="xml" indent="yes"/> 

<xsl:template match="entries"> 
    <entries> 
     <xsl:apply-templates/> 
    </entries> 
</xsl:template> 

<xsl:template match="entry"> 
    <entry> 
     <xsl:attribute name="field"> 
      <xsl:apply-templates select="*"/> 
     </xsl:attribute> 
    </entry> 
</xsl:template> 

Répondre

2

En XSLT général 1.0 et 2.0 travail avec un modèle de données qui tire l'entrée XML en un modèle d'arbre pour permettre une navigation complète XPath , ce qui entraîne une utilisation de la mémoire qui augmente avec la taille du document d'entrée. Donc, sauf si vous augmentez l'espace de mémoire si la taille de votre document actuel conduit à une insuffisance de mémoire, vous ne pouvez pas faire grand-chose, du moins pas en général, il peut y avoir des spécificités XSLT et XSLT spécifiques. code, mais vous ne pouvez pas éviter que le processeur tire d'abord dans le document complet. Nous aurions besoin de voir votre XSLT pour essayer de dire s'il peut être optimisé. Le profilage d'une feuille de style peut aider à identifier les zones à optimiser, mais je ne suis pas sûr que Xalan supporte cela. Et je ne suis pas sûr que cette trace de pile signifie non seulement que Xalan manque de mémoire lors de la construction du DTM (son modèle d'arbre) pour votre grande entrée, dans ce cas, l'optimisation du code XSLT n'aide pas car elle n'est même pas exécutée . Un moyen spécifique à Java que vous pouvez essayer est d'utiliser https://docs.oracle.com/javase/8/docs/api/javax/xml/transform/sax/SAXTransformerFactory.html à la place pour créer un filtre SAX à partir de votre feuille de style et le chaîner avec un Transformer par défaut pour sérialiser le résultat du filtre, je pense que j'ai déjà essayé et trouvé consomme moins de mémoire que l'approche traditionnelle avec un transformateur.

XSLT 3.0 tente de résoudre le problème de mémoire avec la nouvelle approche de streaming (https://www.w3.org/TR/xslt-30/#streaming-concepts), mais jusqu'à présent, il n'y a qu'une seule implémentation avec Saxon 9 EE, un produit commercial. Et en général, une feuille de style n'est pas nécessairement streamable, mais vous devez la réécrire pour la rendre plus stable (si cela est possible, par exemple, le tri des nœuds d'entrée n'est pas possible avec le streaming).

Par exemple, votre feuille de style affiché converti en XSLT 3.0 pour utiliser le streaming (pas de réécriture nécessaire, que nécessaire pour mettre en place le mode par défaut comme streamable) est

<?xml version="1.0" encoding="UTF-8"?> 
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
    xmlns:xs="http://www.w3.org/2001/XMLSchema" 
    xmlns:math="http://www.w3.org/2005/xpath-functions/math" 
    exclude-result-prefixes="xs math" 
    version="3.0"> 

    <xsl:mode streamable="yes"/> 

    <xsl:output method="xml" indent="yes"/> 

    <xsl:template match="entries"> 
     <entries> 
      <xsl:apply-templates/> 
     </entries> 
    </xsl:template> 

    <xsl:template match="entry"> 
     <entry> 
      <xsl:attribute name="field"> 
       <xsl:apply-templates select="*"/> 
      </xsl:attribute> 
     </entry> 
    </xsl:template> 

</xsl:stylesheet> 

et Saxon 9.8 EE et la version bêta de Exselt évaluer cela comme étant fluide.

+0

J'ai ajouté mon fichier 'XSLT', voir s'il vous plaît –

+1

Notez aussi que pour faire cela fonctionner avec Saxon-EE, vous devez modifier légèrement votre code Java pour vous assurer que TransformerFactory que vous utilisez est une instance de Saxon Classe 'StreamableTransformerFactory'. –