2017-09-01 82 views
1

您好我一直在使用Jena进行项目,现在我正在尝试使用Hadoop批量处理来查询图形以存储在纯文件中。apache Jena中使用TDB的Java OutOfMemoryError

我打开一个TDB Dataset然后我用LIMIT和OFFSET查询页面。

我输出文件100000三胞胎每个文件。

然而,在文件10中,性能下降,在文件15中下降了3倍,而在第22个文件中,性能下降到1%。

我的查询是:显示在下面的代码块

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

的查询和写入文件的方法:

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

空变量是那里,因为我没有”不知道那里是否有可能泄漏。

然而第22文件的程序失败,出现以下消息后:

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 

存储器观看者示出了存储器使用的增量查询页面之后:

enter image description here

enter image description here

很明显,耶拿LocalCache正在填补,我已经将Xmx更改为2048米,并将Xms更改为5 12米,结果相同。没有改变。

我需要更多内存吗?

我需要清除一些东西吗?

我是否需要停止该程序并在零件中执行?

我查询错了吗?

OFFSET和它有什么关系吗?

我读了一些旧的邮件发帖,你可以关闭缓存,但我找不到任何方式来做到这一点。有没有办法关闭缓存?

我知道这是一个非常困难的问题,但我很感激任何帮助。

+0

我认为你应该考虑重写这个。请参阅指南:https://stackoverflow.com/help/how-to-ask – Kamran

+1

'2048m'现在没有那么多。为什么你不能简单地增加它?你使用哪个Jena版本? – AKSW

+1

@Nord,只是一个小小的评论:也许你不需要'DISTINCT'。 –

回答

4

显然,耶拿LocalCache被填满

这是TDB节点缓存 - 每个数据集本身通常需要1.5G(2G更好)。该缓存在JVM的生命周期中保持不变。

根据今天的标准,2G的java堆是一个小的Java堆。如果你必须使用一个小堆,你可以尝试在32位模式下运行(在TDB中称为“直接模式”),但是性能较差(主要是因为节点缓存较小,并且在此数据集中有足够的节点来缓存流失一个小缓存)。

节点缓存是造成堆耗尽的主要原因,但查询耗用内存中的其他位置,每个查询在DISTINCT

DISTINCT不一定便宜。它需要记住所看到的所有信息,以了解新行是第一次出现还是已经出现。

Apache Jena确实优化了(TopN查询)的某些情况,但优化的截止点 默认为1000。请参阅代码中的OpTopN

否则它会收集迄今为止所看到的所有行。数据集越远,节点缓存中的数据越多,并且DISTINCT过滤器中的数据集越多。

我需要更多的记忆吗?

是的,更多的堆。每个TDB数据集的最小敏感度为2G,然后无论Java本身需要(例如0.5G)还是加上程序和查询工作空间。

+0

谢谢,我看到类似于你所描述的东西,我查询第21页,它开始缓慢,也许我会放弃DISTINCT,因为在hadoop mapper之后,reduce步骤将允许我检测重复项,我会尝试用你的解决方案4GB和6GB以及后来的12GB时,当我到我的主电脑。 – Nord

+0

我将内存更改为4G并丢弃了'DISTINCT',它运行良好。我将在MapReduce步骤中检查重复项。我认为2GB已经足够用于TDB的任何相关任务,我没有在文档中找到它。谢谢。 – Nord

0

你似乎有内存泄漏的地方,这只是一种猜测,但试试这个:

TDBFactory.release(ds); 

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

+1

在这种情况下,这不太可能有所帮助,因为节点缓存将再次填满。这是每次相同的查询,通过数据库以相同的顺序进行不同的长度。在较长的查询中,节点缓存将以相同的方式填充,并在大致相同的点处爆炸。 – AndyS