2

根据 autocreateDatastoreTxns 的设置,我得到了内存泄漏,为每个查询(读取)创建了以下每个类的一个实例。即 100 个查询创建以下每个类的 100 个实例(DatastoreServiceConfig 除外,它每个查询获取 2 个实例)。

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

// 泄漏 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 中真正的内存泄漏,还是开发环境的现实,或者可能是我自己的错误?

4

3 回答 3

1

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

某些应用程序因其使用性质和数据访问模式而受益于更大的前端实例。这些成本更高,因此您必须测试并权衡收益与增加的成本。

于 2012-09-08T03:47:03.950 回答
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 代码进行任何其他更改。

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

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

于 2014-05-07T13:29:54.407 回答
0

这可能是 GAE 泄漏。它也可能是您的代码中的错误。区分这些情况的唯一方法是追查(可能的)泄漏的实际原因。

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

于 2012-09-08T02:52:35.087 回答