2017-09-01 7 views
1

Hallo Ich habe Jena für ein Projekt verwendet und jetzt versuche ich, ein Diagramm für die Speicherung in einfachen Dateien für Batch-Verarbeitung mit Hadoop abzufragen.Java OutOfMemoryError in Apache Jena mit TDB

Ich öffne ein TDB Dataset und dann suche ich nach Seiten mit LIMIT und OFFSET.

Ich gebe Dateien mit 100000 Drillingen pro Datei aus.

Bei Datei 10. verschlechtert sich jedoch die Leistung und bei Datei 15. sinkt sie um den Faktor 3 und bei der 22. Datei ist die Leistung auf 1% gesunken.

Meine Frage ist:

SELECT DISTINCT ?S ?P ?O WHERE {?S ?P ?O .} LIMIT 100000 OFFSET X

Die Methode, die Abfragen und in eine Datei schreibt im nächsten Codeblock gezeigt:

public boolean copyGraphPage(int size, int page, String tdbPath, String query, String outputDir, String fileName) throws IllegalArgumentException { 
     boolean retVal = true; 
     if (size == 0) { 
      throw new IllegalArgumentException("The size of the page should be bigger than 0"); 
     } 
     long offset = ((long) size) * page; 
     Dataset ds = TDBFactory.createDataset(tdbPath); 
     ds.begin(ReadWrite.READ); 
     String queryString = (new StringBuilder()).append(query).append(" LIMIT " + size + " OFFSET " + offset).toString(); 
     QueryExecution qExec = QueryExecutionFactory.create(queryString, ds); 
     ResultSet resultSet = qExec.execSelect(); 
     List<String> resultVars; 
     if (resultSet.hasNext()) { 
      resultVars = resultSet.getResultVars(); 
      String fullyQualifiedPath = joinPath(outputDir, fileName, "txt"); 
      try (BufferedWriter bwr = new BufferedWriter(new OutputStreamWriter(new BufferedOutputStream(
        new FileOutputStream(fullyQualifiedPath)), "UTF-8"))) { 
       while (resultSet.hasNext()) { 
        QuerySolution next = resultSet.next(); 
        StringBuffer sb = new StringBuffer(); 
        sb.append(next.get(resultVars.get(0)).toString()).append(" "). 
          append(next.get(resultVars.get(1)).toString()).append(" "). 
          append(next.get(resultVars.get(2)).toString()); 
        bwr.write(sb.toString()); 
        bwr.newLine(); 
       } 
       qExec.close(); 
       ds.end(); 
       ds.close(); 
       bwr.flush(); 
      } catch (IOException e) { 
       e.printStackTrace(); 
      } 
      resultVars = null; 
      qExec = null; 
      resultSet = null; 
      ds = null; 
     } else { 
      retVal = false; 
     } 
     return retVal; 
    } 

Die Null-Variablen sind da, weil ich didn‘ t wissen, ob dort ein mögliches Leck war.

jedoch nach der 22. Datei des Programm mit der folgenden Meldung fehlschlägt:

java.lang.OutOfMemoryError: GC overhead limit exceeded 

    at org.apache.jena.ext.com.google.common.cache.LocalCache$EntryFactory$2.newEntry(LocalCache.java:455) 
    at org.apache.jena.ext.com.google.common.cache.LocalCache$Segment.newEntry(LocalCache.java:2144) 
    at org.apache.jena.ext.com.google.common.cache.LocalCache$Segment.put(LocalCache.java:3010) 
    at org.apache.jena.ext.com.google.common.cache.LocalCache.put(LocalCache.java:4365) 
    at org.apache.jena.ext.com.google.common.cache.LocalCache$LocalManualCache.put(LocalCache.java:5077) 
    at org.apache.jena.atlas.lib.cache.CacheGuava.put(CacheGuava.java:76) 
    at org.apache.jena.tdb.store.nodetable.NodeTableCache.cacheUpdate(NodeTableCache.java:205) 
    at org.apache.jena.tdb.store.nodetable.NodeTableCache._retrieveNodeByNodeId(NodeTableCache.java:129) 
    at org.apache.jena.tdb.store.nodetable.NodeTableCache.getNodeForNodeId(NodeTableCache.java:82) 
    at org.apache.jena.tdb.store.nodetable.NodeTableWrapper.getNodeForNodeId(NodeTableWrapper.java:50) 
    at org.apache.jena.tdb.store.nodetable.NodeTableInline.getNodeForNodeId(NodeTableInline.java:67) 
    at org.apache.jena.tdb.store.nodetable.NodeTableWrapper.getNodeForNodeId(NodeTableWrapper.java:50) 
    at org.apache.jena.tdb.solver.BindingTDB.get1(BindingTDB.java:122) 
    at org.apache.jena.sparql.engine.binding.BindingBase.get(BindingBase.java:121) 
    at org.apache.jena.sparql.engine.binding.BindingProjectBase.get1(BindingProjectBase.java:52) 
    at org.apache.jena.sparql.engine.binding.BindingBase.get(BindingBase.java:121) 
    at org.apache.jena.sparql.engine.binding.BindingProjectBase.get1(BindingProjectBase.java:52) 
    at org.apache.jena.sparql.engine.binding.BindingBase.get(BindingBase.java:121) 
    at org.apache.jena.sparql.engine.binding.BindingBase.hashCode(BindingBase.java:201) 
    at org.apache.jena.sparql.engine.binding.BindingBase.hashCode(BindingBase.java:183) 
    at java.util.HashMap.hash(HashMap.java:338) 
    at java.util.HashMap.containsKey(HashMap.java:595) 
    at java.util.HashSet.contains(HashSet.java:203) 
    at org.apache.jena.sparql.engine.iterator.QueryIterDistinct.getInputNextUnseen(QueryIterDistinct.java:106) 
    at org.apache.jena.sparql.engine.iterator.QueryIterDistinct.hasNextBinding(QueryIterDistinct.java:70) 
    at org.apache.jena.sparql.engine.iterator.QueryIteratorBase.hasNext(QueryIteratorBase.java:114) 
    at org.apache.jena.sparql.engine.iterator.QueryIterSlice.hasNextBinding(QueryIterSlice.java:76) 
    at org.apache.jena.sparql.engine.iterator.QueryIteratorBase.hasNext(QueryIteratorBase.java:114) 
    at org.apache.jena.sparql.engine.iterator.QueryIteratorWrapper.hasNextBinding(QueryIteratorWrapper.java:39) 
    at org.apache.jena.sparql.engine.iterator.QueryIteratorBase.hasNext(QueryIteratorBase.java:114) 
    at org.apache.jena.sparql.engine.iterator.QueryIteratorWrapper.hasNextBinding(QueryIteratorWrapper.java:39) 
    at org.apache.jena.sparql.engine.iterator.QueryIteratorBase.hasNext(QueryIteratorBase.java:114) 

Disconnected from the target VM, address: '127.0.0.1:57723', transport: 'socket' 

Process finished with exit code 255 

Der Speicher Betrachter einen Zuwachs in der Speichernutzung zeigt nach einer Seite Abfrage:

enter image description here

enter image description here

Es ist klar, dass Jena LocalCache füllt, habe ich die Xmx auf 2048m und Xms auf 5 geändert 12m mit dem gleichen Ergebnis. Nichts hat sich geändert.

Benötige ich mehr Speicher?

Muss ich etwas löschen?

Muss ich das Programm stoppen und es in Teilen tun?

Ist meine Abfrage falsch?

Hat der OFFSET irgendetwas damit zu tun?

Ich habe in einigen alten Posts gelesen, dass Sie den Cache ausschalten können, aber ich konnte keine Möglichkeit finden, es zu tun. Gibt es eine Möglichkeit, den Cache auszuschalten?

Ich weiß, es ist eine sehr schwierige Frage, aber ich schätze jede Hilfe.

+0

Ich denke, Sie sollten darüber nachdenken, dies neu zu schreiben. Siehe die Richtlinien: https://stackoverflow.com/help/how-to-ask – Kamran

+1

'2048m' ist nicht so viel heutzutage. Warum kannst du es nicht einfach erhöhen? Und welche Jena Version verwendest du? – AKSW

+1

@Nord, nur ein kleiner Kommentar: vielleicht brauchst du 'DISTINCT' nicht. –

Antwort

4

Es ist klar, dass Jena Localcache bis füllt

Dies ist der TDB-Knoten-Cache - er benötigt normalerweise 1,5 G (2G ist besser) pro Datensatz. Dieser Cache bleibt für die Lebensdauer der JVM bestehen.

Ein Java-Heap von 2G ist ein kleiner Java-Heap nach heutigen Standards. Wenn Sie einen kleinen Heap verwenden müssen, können Sie versuchen, im 32-Bit-Modus zu arbeiten (in TDB "Direct Mode" genannt), aber dies ist weniger performant (hauptsächlich weil der Knoten-Cache kleiner ist und Sie in diesem Datensatz genug Knoten haben, um Cache zu verursachen Abwanderung für einen kleinen Cache).

Der Knotencache ist die Hauptursache für die Erschöpfung des Heapspeichers, aber die Abfrage verbraucht Speicher an anderer Stelle pro Abfrage in DISTINCT.

DISTINCT ist nicht unbedingt billig. Es muss sich an alles erinnern, was es gesehen hat, um zu wissen, ob eine neue Zeile das erste Vorkommen ist oder bereits gesehen wurde.

Apache Jena optimiert einige Fälle von (eine TopN-Abfrage), aber der Cutoff für die Optimierung ist standardmäßig 1000. Siehe OpTopN im Code.

Sonst sammelt es alle Zeilen, die bisher gesehen wurden. Je weiter Sie durch das Dataset gehen, desto mehr im Knoten-Cache und auch mehr als im DISTINCT-Filter.

Benötige ich mehr Speicher?

Ja, mehr Heap. Das vernünftige Minimum ist 2G pro TDB-Dataset und dann, was auch immer Java selbst benötigt (sagen wir 0,5G) und plus Ihren Programm- und Abfrage-Arbeitsbereich.

+0

Danke, ich habe etwas ähnliches gesehen, was du beschreibst, ich frage Seite 21 und es begann langsam, vielleicht werde ich die DISTINCT fallen lassen, da nach Hadoop-Mapper der Reduzierungsschritt mir erlauben wird, Duplikate zu erkennen, ich werde deine Lösung mit ausprobieren 4GB und 6GB und später 12GB, wenn ich zu meinem Hauptcomputer komme. – Nord

+0

Ich änderte den Speicher auf 4G und ließ die 'DISTINCT' fallen und es läuft gut. Ich werde im MapReduce-Schritt nach Duplikaten suchen. Ich dachte, 2GB wäre genug für eine verwandte Aufgabe für TDB Ich fand es nicht in der Dokumentation. Vielen Dank. – Nord

0

Sie scheinen irgendwo Speicherverlust zu haben, das ist nur eine Vermutung, aber versuchen Sie dies:

TDBFactory.release(ds); 

REF: https://jena.apache.org/documentation/javadoc/tdb/org/apache/jena/tdb/TDBFactory.html#release-org.apache.jena.query.Dataset-

+1

Dies wird in diesem Fall wahrscheinlich nicht helfen, da der Knoten-Cache wieder voll wird. Dies ist die gleiche Abfrage jedes Mal, wobei die Datenbank in der gleichen Reihenfolge unterschiedliche Längen hat. Der Knoten-Cache füllt sich bei der längeren Abfrage genauso und explodiert ungefähr am selben Punkt. – AndyS

Verwandte Themen