【问题标题】:Neo4j embedded out of memory at node/relationship creationNeo4j 在节点/关系创建时嵌入内存不足
【发布时间】:2014-07-02 10:51:39
【问题描述】:

我一直在研究 Neo4j 的生物信息学问题。我创建了大约 20000 个节点。这些节点 每个应该与大约 100 个节点相关。

我想将 Java 核心 API 与嵌入式 Neo4j 数据库一起使用,如 [Java 教程] 中所述 (http://docs.neo4j.org/chunked/milestone/tutorials-java-embedded-hello-world.html)

在添加派生节点和关系之前,我必须先查询数据库以获取现有节点。

我很快就遇到了过多的内存消耗。我在这里附上了一个使 Neo4J 崩溃的 Java 方法。 拜托,你能给我一个关于如何解决这个内存问题的提示吗?解决这种情况的最佳做法是什么?

我附上内存使用图(来自 VisualVM 的快照)来说明内存使用、、。

配置:

  Platform : Windows-7 win32,  java-1.7.0_51 (Program arguments -Xms512m -Xmx1024m)
  neo4j.properties
    use_memory_mapped_buffers=true
    neostore.nodestore.db.mapped_memory=100M
    neostore.relationshipstore.db.mapped_memory=150M
    neostore.propertystore.db.mapped_memory=150M
    neostore.propertystore.db.strings.mapped_memory=150M
    neostore.propertystore.db.arrays.mapped_memory=150M

  neo4j-wrapper.conf
    wrapper.java.additional=-XX:+UseConcMarkSweepGC
    wrapper.java.additional=-XX:+CMSClassUnloadingEnabled
    wrapper.java.initmemory=512
    wrapper.java.maxmemory=1024

提前致谢, 最好的问候

代码:值的限制不同,平均值应该在100左右。

static void stackoverflowNativeAPIMemoryIssue() {
    String DB_PATH = "C:/neo4j/Neo4j-2.1.2/data/graph.db";
    GraphDatabaseService db = new GraphDatabaseFactory()
        .newEmbeddedDatabase(DB_PATH);        
    // *** query
    String query = "match (n:ExistingNode) return n;";            
    ExecutionEngine engine = new ExecutionEngine(db);        
    ExecutionResult result;        
    Label labelFrom = DynamicLabel.label("From");        
    result = engine.execute(query);        
    Iterator<Node> n_column = result.columnAs("n");
    Node nodeFrom = null;
    Relationship relationship = null;        
    int count = 0;        
    int i = 0;
    for (Node nodeTo : IteratorUtil.asIterable(n_column)) {
      // loop which makes the code break!
      //for (i = 0; i < 5; i++) {
        try (Transaction tx = db.beginTx()) {
          ++count;
          nodeFrom = db.createNode(labelFrom);
          nodeFrom.setProperty("name", "name-" + count + "-" + i);

          relationship = nodeFrom.createRelationshipTo(nodeTo,
              Relation.MY_RELATION);
          relationship.setProperty("name", "relation-" + count
              + "- " + i);
          tx.success();
        }    
      //}
    }
    db.shutdown();
    }

无循环:程序一直运行到结束...

循环 5 -> 内存扩展,但进程正常终止。

循环 10 次 -> 内存不足 没有节点,也没有创建关系,尽管应该在每个节点上触发事务并创建关系。

Exception in thread "GC-Monitor" Exception in thread "main" java.lang.OutOfMemoryError: 
Java heap space
at java.lang.AbstractStringBuilder.append(Unknown Source)
at java.lang.StringBuilder.append(Unknown Source)
at org.neo4j.kernel.impl.cache.MeasureDoNothing.run(MeasureDoNothing.java:84)
java.lang.OutOfMemoryError: Java heap space
at org.neo4j.kernel.impl.util.VersionedHashMap.put(VersionedHashMap.java:185)
at java.util.Collections$SetFromMap.add(Unknown Source)
at org.neo4j.kernel.impl.util.DiffSets.add(DiffSets.java:100)
at org.neo4j.kernel.impl.api.state.TxStateImpl.nodeDoCreate(TxStateImpl.java:363)
at org.neo4j.kernel.impl.api.StateHandlingStatementOperations.nodeCreate(StateHandlingStatementOperations.java:101)
at org.neo4j.kernel.impl.api.ConstraintEnforcingEntityOperations.nodeCreate(ConstraintEnforcingEntityOperations.java:390)
at org.neo4j.kernel.impl.api.LockingStatementOperations.nodeCreate(LockingStatementOperations.java:208)
at org.neo4j.kernel.impl.api.OperationsFacade.nodeCreate(OperationsFacade.java:500)
at org.neo4j.kernel.InternalAbstractGraphDatabase.createNode(InternalAbstractGraphDatabase.java:1125)

【问题讨论】:

  • 切换到另一个选项卡并检查打开了多少 ExecutionResult,消耗了多少 JVM 使用的内存,您缺少所有 JDBC 对象的 close() ,否则它们将永远留在那里,只是增加JVM内存
  • 谢谢!抱歉,您如何查看 ExecutionResult 实例编号(Sampler>Memory?我只找到 org.neo4j.cypher.internal.complier.v2_1.PipeExecutionResult :最多 796 个实例)。我不使用 JDBC(来自 neo4j-jdbc 项目),因为我使用嵌入式数据库恕我直言......

标签: java neo4j out-of-memory


【解决方案1】:

我在一个运行很长事务的程序中遇到了类似的问题。

我的程序基本上是逐行解析一个大的 CSV 文件,并为它解析的每一行注入节点和关系。这个大的while循环被包含在一个事务块中。

正如你所描述的那样,我发生了内存泄漏。

但我发现,使用 VisualVM 时,当 while 循环结束时,内存堆大小大幅下降。所以我想知道:“在那个 while 循环中永远存在的对象是什么?”答案是 Transaction 对象。

所以我修补了我的程序,为文件解析循环的每次迭代创建一个事务,虽然这会降低解析的性能,但它解决了内存泄漏,运行一些迭代后堆大小现在是稳定的。

希望对你有帮助。

如果 Neo4j 专家能够阐明 Transaction 泄漏的原因,我们将不胜感激。

【讨论】:

  • 每个事务的内容都完全保存在内存中,直到事务关闭,因为在事务成功提交之前不能将它们放到磁盘上。当两个打开的事务以不同的方式打开节点时,这将启用回滚和解析。
【解决方案2】:

请注意,在 Windows 平台上,mapped_memory 是 JVM 堆的一部分。您总共有 700M 分配给映射内存和 1G 的最大堆大小 - 剩下的内存更少。

增加最大堆或缩小映射内存。

【讨论】:

  • 使用嵌入式数据库时是否相关?
【解决方案3】:

上面的代码存在更广泛的问题。

结果 = engine.execute(query);

至少在概念上,它本身应该在一个事务中。它返回一个惰性求值的迭代器,每次调用这个迭代器都需要在它返回的节点上进行读锁。因此,您实际上是在尝试将作为一个打开事务的结果的节点传递给编辑它们的第二个事务。

假设在您的代码中间,在您生成迭代器后,我进行了第三次交易,删除了您的所有节点,那会发生什么?

基本上,您不应该尝试将节点对象从一个事务传递到另一个事务。 Neo4j 是一个服务器,并且不能假设在您的事务关闭之后,但在您的第二个事务打开之前没有其他用户会编辑这些用户。

我怀疑在服务器端,一整套死锁解决例程正在发挥作用,以处理在同一个节点对象上有多个打开事务的事实,其中至少一个是写入事务。这可能是您感知到的泄漏的原因。

尝试将您的执行引擎代码放入事务中,然后在单个事务中迭代循环。在单个事务中创建几千个实体完全没问题,并且堆空间开销最小。

【讨论】:

    猜你喜欢
    • 2022-07-20
    • 1970-01-01
    • 1970-01-01
    • 2016-07-03
    • 1970-01-01
    • 1970-01-01
    • 2016-05-01
    • 1970-01-01
    • 2014-08-27
    相关资源
    最近更新 更多