这是GAE内存泄漏的证据吗?

时间:2012-09-08 00:55:35

标签: java google-app-engine memory-leaks jdo visualvm

根据autocreateDatastoreTxns的设置,我得到内存泄漏 下面为每个查询创建的每个类的一个实例(读取)。 即100个查询创建下面每个类的100个实例(使用 DatastoreServiceConfig的异常,每个查询获得2个实例。

我在开发环境中使用Java VisualVM分析器发现了这一点。我这样做的原因 就是在生产中,我们的实例堆大小不断增长(通常变得太大了) 在10-20万个请求之后)最终导致响应缓慢和实例重启。 我不知道这是否是原因,但它是我迄今为止能够识别的第一次泄漏。

//使用datanucleus泄漏.appengine.autoCreateDatastoreTxns = false

org.datanucleus.store.appengine.jdo.DatastoreJDOPersistenceManager
org.datanucleus.store.appengine.KeyRegistry
org.datanucleus.store.appengine.EmualtedXARResource
org.datanucleus.store.appengine.DatastoreConnectionFactoryImpl$DatastoreManagedConnection

//使用datanucleus泄漏.appengine.autoCreateDatastoreTxns = true

com.google.appengine.api.datastore.DatastoreServiceConfig  // 2 instances per query
org.datanucleus.store.appengine.jdo.DatastoreJDOPersistenceManager
com.google.appengine.api.datastore.AsyncDatastoreServiceImpl
com.google.appengine.api.datastore.DatastoreServiceImpl
org.datanucleus.store.appengine.jdo. DatastoreJDOTransation
com.google.appengine.api.datastore.DatastoreXARResource
com.google.appengine.api.datastore.DatastoreProperty
com.google.appengine.api.datastore.KeyRegistry
com.google.appengine.api.datastore.DatastoreConnectionFactoryImpl$DatastoreManagedConnection
com.google.appengine.api.datastore.TransactionStackImpl$ThreadLocalTransactionStack$StaticMember
com.google.appengine.api.datastore.TransactionStackImpl
org.datanucleus.store.appengine.RuntimeExceptionWrapperingDatastoreService

这是我的代码:

PersistenceManager pm = PMF.get().getPersistenceManager();
try {
    account = pm.detachCopy(pm.getObjectById(Account.class, accountKey));
} catch (javax.jdo.JDOObjectNotFoundException ex) {
    account = null;
} finally {
    pm.close();
}

任何想法/想法?这是Google AppEngine中真正的内存泄漏,还是仅仅是开发环境的生命现实,或者是我自己的错误?

3 个答案:

答案 0 :(得分:1)

开发应用程序服务器旨在模拟真实应用程序服务器的语义,以便您可以合理保真地进行开发。这不包括内存行为,特别是关于数据存储区。开发服务器倾向于以真正的应用程序服务器没有的方式将内容保存在内存中。分析开发服务器对于解决应用程序端的泄漏仍然很有用,但是不会为应用程序服务器端的泄漏提供很多指导。不过,我们会留意这些。而且随着时间的推移,堆会碎裂。

根据其使用和数据访问模式的性质,某些应用程序可从较大的前端实例中受益。这些成本更高,因此您必须根据增加的成本测试和加权。

答案 1 :(得分:1)

据我所见,生产AppEngine上存在JDO内存泄漏。

这导致实例中的延迟随着时间的推移而增加。这个问题已经存在了一段时间,并没有正式提供明显的解决方案,所以下面是一个PMF类的快速解决方案,我已经在生产系统中使用了一年多,现在解决了延迟增加的问题(内存使用量仍然增长但速度不快。

import javax.jdo.JDOHelper;
import javax.jdo.PersistenceManagerFactory;

public final class PMF {

    private static final int RECYCLE_POINT = 5000;
    private static final int BUFFER_ZONE = RECYCLE_POINT / 10;
    private static int pmfcount = 1;
    private static int marker = Integer.MIN_VALUE;
    private static PersistenceManagerFactory pmfInstancePrevious = null;
    private static PersistenceManagerFactory pmfInstance = JDOHelper
            .getPersistenceManagerFactory("transactions-optional");

    private PMF() {
    }

    public static PersistenceManagerFactory get() {
        int icount = pmfcount;
        if (icount % RECYCLE_POINT == 0) {
            synchronized (pmfInstance) {
                pmfInstancePrevious = pmfInstance;
                marker = icount;
                pmfInstance = JDOHelper
                    .getPersistenceManagerFactory("transactions-optional");
            }
        }
        if(marker+BUFFER_ZONE == icount) {
            if (null != pmfInstancePrevious) pmfInstancePrevious.close();
        }
        pmfcount++;
        return pmfInstance;
    }
}

代码是线程安全的(同步的),可以与多线程实例一起使用,而无需对JDO代码进行任何其他更改。

您可以调整RECYCLE_POINT,具体取决于您对PMF.get()的使用情况,但我发现5000次调用是获得新实例的好点。

如果您在使用时收到PMF关闭的任何消息,BUFFER_ZONE也很重要。这意味着您将保持PersistenceManagerFactory的处理或执行非常长的请求。相反,您应该在每次请求开始时使用PMF.get()(甚至在每个请求中多次使用)。如果你想让前一个PMF实例保持更长时间,但总是只是RECYCLE_POINT的一小部分,请增加BUFFER_ZONE。

答案 2 :(得分:0)

这可能是GAE泄漏。它也可能是代码中的错误。区分案例的唯一方法是追踪(可能)泄漏的实际原因。

我应该补充一点,泄漏可能存在于代码的另一部分;例如如果您在其他地方使用持久性管理器。 FWIW,你问题中的代码看起来还不错......