2017-09-01 3 views
1

Salut J'ai utilisé Jena pour un projet et maintenant j'essaye d'interroger un graphique pour le stockage dans des fichiers simples pour le traitement par lots avec Hadoop.Java OutOfMemoryError dans apache Jena utilisant TDB

J'ouvre un TDB Dataset puis j'interroge par pages avec LIMIT et OFFSET.

Je génère des fichiers avec 100 000 triplets par fichier.

Cependant, au niveau du fichier 10, la performance se dégrade et au 15ème fichier, elle diminue d'un facteur 3 et au 22ème fichier, les performances sont réduites à 1%.

Ma requête est:

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

La méthode des requêtes et écrit dans un fichier est affiché dans le bloc de code suivant:

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

Les variables nulles sont là parce que je n » Je ne sais pas s'il y avait une fuite possible là-bas.

Cependant, après le fichier 22 du programme échoue avec le message suivant:

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 

Le spectateur mémoire montre une augmentation de l'utilisation de la mémoire après l'interrogation d'une page:

enter image description here

enter image description here

Il est clair que Jena LocalCache se remplit, j'ai changé le Xmx à 2048m et Xms à 5 12m avec le même résultat. Rien n'a changé.

Ai-je besoin de plus de mémoire?

Dois-je effacer quelque chose?

Dois-je arrêter le programme et le faire en partie?

Ma requête est erronée?

Est-ce que OFFSET a quelque chose à voir avec cela?

J'ai lu dans certains anciens messages que vous pouvez désactiver le cache, mais je n'ai trouvé aucun moyen de le faire. Y a-t-il un moyen de désactiver le cache?

Je sais que c'est une question très difficile mais j'apprécie toute aide.

+0

Je pense que vous devriez envisager de réécrire cela. Voir les directives: https://stackoverflow.com/help/how-to-ask – Kamran

+1

'2048m' n'est pas beaucoup de nos jours. Pourquoi ne pouvez-vous pas simplement l'augmenter? Et quelle version de Jena utilisez-vous? – AKSW

+1

@Nord, juste un commentaire mineur: peut-être que vous n'avez pas besoin de DISTINCT. –

Répondre

4

Il est clair que Jena LocalCache se remplit

Ceci est le cache de noeud TDB - il nécessite généralement 1,5G (2G est mieux) par ensemble de données lui-même. Ce cache persiste pendant la durée de vie de la machine virtuelle Java.Un tas Java de 2G est un petit segment de Java par rapport aux normes d'aujourd'hui. Si vous devez utiliser un petit tas, vous pouvez essayer en mode 32 bits (appelé "mode direct" dans TDB) mais cela est moins performant (principalement parce que le cache des nœuds est plus petit et que vous avez suffisamment de nœuds pour provoquer le cache baratte pour une petite cache).

Le cache de noeud est la cause principale de l'épuisement du tas, mais la requête consomme de la mémoire ailleurs, par requête, dans DISTINCT.

DISTINCT n'est pas nécessairement pas cher. Il doit se souvenir de tout ce qu'il a vu pour savoir si une nouvelle ligne est la première occurrence ou déjà vue. Apache Jena optimise certains cas de (une requête TopN) mais la valeur de coupure pour l'optimisation est 1000 par défaut. Voir OpTopN dans le code.

Sinon, il collecte toutes les lignes vues jusqu'à présent. Plus vous parcourez l'ensemble de données, plus il y en a dans le cache des noeuds et plus il y en a dans le filtre DISTINCT.

Ai-je besoin de plus de mémoire?

Oui, plus de tas. Le minimum raisonnable est de 2G par jeu de données TDB et ensuite tout ce que Java lui-même nécessite (disons, 0.5G) et plus votre espace de travail de programme et de requête.

+0

Merci, j'ai vu quelque chose de similaire à ce que vous décrivez, je demande la page 21 et il a commencé lentement, peut-être je laisser tomber le DISTINCT puisque hadoop mapper l'étape de réduction me permettra de détecter les doublons, je vais essayer votre solution 4 Go et 6 Go et plus tard 12 Go quand j'arrive à mon ordinateur principal. – Nord

+0

J'ai changé la mémoire en 4G et j'ai laissé tomber 'DISTINCT' et ça marche bien. Je vais vérifier les doublons à l'étape MapReduce. Je pensais que 2 Go était suffisant pour toute tâche connexe pour TDB je ne l'ai pas trouvé dans la documentation. Merci. – Nord

0

Vous semblez avoir quelque part fuite de mémoire, cela est juste une supposition, mais essayez ceci:

TDBFactory.release(ds); 

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

+1

Dans ce cas, il est peu probable que le cache de noeud se remplisse à nouveau. C'est la même requête à chaque fois, en parcourant des longueurs différentes à travers la base de données dans le même ordre. Le cache de noeud va se remplir de la même manière sur la requête plus longue et exploser à peu près au même point. – AndyS