【问题标题】:Is this evidence of a GAE memory leak?这是 GAE 内存泄漏的证据吗?
【发布时间】:2012-09-01 20:55:14
【问题描述】:

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

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

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

【问题讨论】:

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


    【解决方案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。

    【讨论】:

      【解决方案2】:

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

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

      【讨论】:

      • 是的,我担心内存分析的生产与开发会出现这种情况......是的,我们已经转移到 F2 实例,因为额外的内存有助于实例持续更长时间,但内存泄漏似乎相当大,所以它只是一个小小的权宜之计。
      • 我将继续在我们自己的代码中寻找漏洞,但是您是否有任何关于从其他 GAE 实例堆增长如此之多的经验中寻找的提示?我在任何地方都没有找到任何关于它的讨论 - 很少见吗?
      【解决方案3】:

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

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

      【讨论】:

        猜你喜欢
        • 2011-02-20
        • 2013-08-12
        • 2013-01-08
        • 2013-11-12
        • 2015-05-04
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多