Java neo4j、REST和内存
我已经在Jersey tomcat for REST API下使用neo4j java嵌入式版本部署了一个应用程序。 通过使用jconsole测量内存使用情况,我注意到每个REST调用都会增加200Mb内存(我认为这是因为整个图形都被加载到内存中)。因此,只需5次调用,服务器就可以分配1Gb的内存,这是非常多的!要清理内存,我必须等待垃圾收集器(阈值设置为1Gb) 这是因为我使用的是Neo4JJava嵌入式版本,还是因为我犯了严重的错误?当API调用结束时,我应该如何释放内存 下面是示例代码:Java neo4j、REST和内存,java,memory-management,jersey,neo4j,Java,Memory Management,Jersey,Neo4j,我已经在Jersey tomcat for REST API下使用neo4j java嵌入式版本部署了一个应用程序。 通过使用jconsole测量内存使用情况,我注意到每个REST调用都会增加200Mb内存(我认为这是因为整个图形都被加载到内存中)。因此,只需5次调用,服务器就可以分配1Gb的内存,这是非常多的!要清理内存,我必须等待垃圾收集器(阈值设置为1Gb) 这是因为我使用的是Neo4JJava嵌入式版本,还是因为我犯了严重的错误?当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();
}
谢谢,
达涅利
--------完整类代码----------
然后我通过浏览器调用REST服务,如下http://localhost:8080/API?q=test
更新为SINGLETON
Java不一定会在可以进行GCD时立即回收内存。您可能会发现,在接近堆限制之前,您不会看到显著的GC操作。因此,即使将堆限制设置为10gb,也可能会发现内存再次增加。这不一定是个问题
<>但是,为了解决使用NX内存的NEO问题,您应该考虑在所有端点之间共享嵌入式实例。也许可以通过将其移动到服务类并在端点之间拥有一个共享实例来实现。如果您将Spring与Jersey一起使用,那么这将很容易做到,因为您可以通过Spring将其连接起来。如果您不使用Spring或Guice,您可以通过使用静态成员和neo4J对象的静态初始化来解决这一问题。如果使用该static是一个交易破坏者,另一个选择是使用静态工厂获取该neo4J对象的单例实例…您不能为每个请求创建neo4J实例。请只创建一次,然后将其传入,要么在静态字段中使用hacky(因为您的资源实例是为每个请求重新创建的),要么在Neo4j服务器中使用注入了
@Context
的提供程序
关于内存使用。Neo4j根据您的使用情况建立内部缓存,以便下次更快地为相同的查询提供服务。因此,这可能会占用一些内存
顺便说一句,你的图有多大?你做的典型操作是什么?是的,你是对的,如果我把堆限制提高一点,行为不变,仍然需要等待GC达到堆的70%以释放内存。具体的问题是,在大多数情况下,当我达到高内存阈值tomcat时,应用程序停止工作,因为我得到一个空异常(我怀疑没有更多内存来实例化一个新的neo实例)。我原以为生产环境的唯一解决方案是将所有产品都转换为neo服务器版本,但您给了我希望,我可以继续使用嵌入式版本。我正在研究如何共享neo实例,但我没有使用Spring,我应该切换到它吗?切换到spring是一个更大的主题:)您肯定不希望每次访问DB时都创建一个新的驱动程序实例。如果您希望得到反馈,请发布您的NPE。感谢您的提示,是否有任何关于如何以静态方式使用neo或Jersey作为singleton的参考资料?如果您想要快速而肮脏的解决方案,请将
GraphDatabaseService
声明为静态成员变量,并在静态初始化块中初始化它。否则,将GraphDatabaseService
初始化为单例。这将允许您在应用程序中共享该实例。您可能想在这里详细了解一下单例模式()。DI使这变得非常简单,但如果你不想添加DI框架,你可以根据维基百科中的示例轻松编写自己的代码,或者按照@MichaelHunger的建议使用Jersey的提供者。好的,我将研究Jersey的注入工作原理。您认为这种方法是否可以在有大量用户请求的生产环境中使用,或者可能存在其他问题和限制(如使用多个实例时的锁存储)?我使用的是java嵌入式版本。db大约为100mb,用于存储一些节点信息和使用cypher进行的一些遍历。是的,它应该像这样使用(使用@Context注入),特别是在生产中。我已经按照您的建议实现了静态单例解决方案,现在只创建了一次实例,并且一切都很顺利。但是,我必须去掉shutdown()方法。考虑到我的申请,它错了吗?而且,即使一切正常,我使用的是一个neo实例,每次调用的内存仍然会增加300mb。最终,使用@Context与实现的静态单例解决方案相比有什么优势?谢谢能否更新代码示例以显示当前状态?在请求过程中你在做什么?我已经用singleton和示例cypher代码更新了代码。你认为内存会因为cypher引擎不是singleton而变得如此庞大吗?最好是使用ServletContextListener来设置数据库和执行引擎,并在取消部署时将其关闭。使用静态变量和方法访问这两者。
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();
}
} );
}
}
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;
}
}