2017-09-01 131 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