2

我已经在 J​​ersey tomcat 下为 REST API 部署了一个使用 neo4j java 嵌入式版本的应用程序。通过使用 jconsole 测量内存使用情况,我注意到每个 REST 调用都会增加 200Mb 的内存(我认为这是因为整个图形被加载到内存中)。因此,只需 5 次调用,服务器就分配了 1Gb 的内存,这是很多!要清理内存,我必须等待垃圾收集器(阈值设置为 1Gb)。

这是正常行为,因为我使用的是 neo4j java 嵌入式版本,还是我做错了什么?当 API 调用结束时,我应该怎么做才能释放内存?

这里示例代码:

@GET
@Produces(MediaType.APPLICATION_JSON + ";charset=utf-8")
public Response getApi( @QueryParam("q")    String query){
try{
// new neo instance here with EmbeddedGraphDatabase
... some code
// stop neo
}catch(Exception ex){
// stop neo
}
return response.ok("json data here").build();   
}

谢谢,丹尼尔

-------- 完整的类代码 ----------

 import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;

import org.neo4j.graphdb.GraphDatabaseService;
import org.neo4j.kernel.EmbeddedGraphDatabase;

@Path("/API")
public class API {

     @GET
     @Produces(MediaType.APPLICATION_JSON)
     public Response apiCall(@QueryParam("q") String query){
        GraphDatabaseService graphDb; 
        try{
            // start neo
            graphDb = new EmbeddedGraphDatabase( "/var/neo4jdb/" );
            this.registerShutdownHook( graphDb );

            // API + NEO code here..

            // stop neo
            graphDb.shutdown();

        }catch(Exception ex){
            // stop neo
            graphDb.shutdown();
        }

        Response response = null;
        return response.ok("This is your query: "+query).build(); 

     }

    /**
     * Server shutdown
     */
    public void registerShutdownHook( final GraphDatabaseService graphDb ){
        // Registers a shutdown hook for the Neo4j instance so that it
        // shuts down nicely when the VM exits (even if you "Ctrl-C" the
        // running example before it's completed)
        Runtime.getRuntime()
                .addShutdownHook( new Thread()
                {
                    @Override
                    public void run()
                    {
                        graphDb.shutdown();
                    }
                } );
    }

}

然后我像这样通过浏览器调用 REST 服务http://localhost:8080/API?q=test

更新单例

     import javax.ws.rs.GET;
    import javax.ws.rs.Path;
    import javax.ws.rs.Produces;
    import javax.ws.rs.QueryParam;
    import javax.ws.rs.core.MediaType;
    import javax.ws.rs.core.Response;

    import org.neo4j.graphdb.GraphDatabaseService;
    import org.neo4j.kernel.EmbeddedGraphDatabase;

    @Path("/API")
    public class API {

         @GET
         @Produces(MediaType.APPLICATION_JSON)
         public Response apiCall(@QueryParam("q") String query){
            GraphDatabaseService graphDb; 
            try{
                // start neo
                Neo4jSingleton neo4jInstance = new Neo4jSingleton();
                GraphDatabaseService graphDb = null;
                graphDb = neo4jInstance.getInstance(DB_PATH);
                this.registerShutdownHook( graphDb );

                // API + NEO code here..
                // cypher query
                        ExecutionEngine engine = new ExecutionEngine(graphDb); 
                        String queryString = "Cypher query code";
                        ExecutionResult result = engine.execute( queryString );
                        // fetch results here..

                // never stop neo now with singleton


            }catch(Exception ex){
                // stop neo
                graphDb.shutdown();
            }

            Response response = null;
            return response.ok("This is your query: "+query).build(); 

         }

        /**
         * Server shutdown
         */
        public void registerShutdownHook( final GraphDatabaseService graphDb ){
            // Registers a shutdown hook for the Neo4j instance so that it
            // shuts down nicely when the VM exits (even if you "Ctrl-C" the
            // running example before it's completed)
            Runtime.getRuntime()
                    .addShutdownHook( new Thread()
                    {
                        @Override
                        public void run()
                        {
                            graphDb.shutdown();
                        }
                    } );
        }

    }
public class Neo4jSingleton {

    private static GraphDatabaseService db;

    public Neo4jSingleton() {

    }

    /*
     * il metodo di restituire un'unica istanza
     * contenente il database neo4j
     */
    public static GraphDatabaseService getInstance(String DB_PATH)
      {

        //Boolean isDbChange=verifyDbChange();

        if (db == null /*|| isDbChange*/)
        {
          db = new EmbeddedGraphDatabase(DB_PATH);
        }

        return db;
      }
}
4

3 回答 3

3

Java 不一定会在内存可用于 GC 时立即回收。您可能会发现,在接近堆限制之前,您不会看到显着的 GC 操作。因此,即使您将堆限制设置为 10gb,您也可能会发现内存再次增加。这不一定是个问题。

但是,要解决 neo 使用 N x Memory 的问题,您应该考虑在所有端点之间共享嵌入式实例。也许通过将其移动到服务类并在端点之间具有共享实例。如果您将 Spring 与 Jersey 一起使用,那么这很容易做到,因为您可以通过 Spring 将其连接起来。

于 2012-08-12T09:18:11.040 回答
2

您不能为每个请求创建 Neo4j 实例。请只创建一次,然后将其传入,或者在静态字段中使用 hacky(因为您是为每个请求重新创建资源实例),或者在 Neo4j 服务器中使用注入@Context.

关于内存使用。Neo4j 会根据您的使用情况建立内部缓存,以便下次更快地处理相同的查询。因此,这可能相当于一些已用内存。

顺便提一句。你的图表有多大,你做的典型操作是什么?

于 2012-08-13T11:58:48.220 回答
1

如果您不使用 Spring 或 Guice,则可以通过为 neo4J 对象使用静态成员和静态初始化来解决此问题。如果拥有该静态是一个交易破坏者,另一种选择是使用静态工厂来获取该 neo4J 对象的单例实例......

于 2012-08-12T23:05:15.993 回答