2012-09-08 13 views
2

根據autocreateDatastoreTxns的設置,我得到一個內存泄漏,其中 爲每個查詢(讀取)創建了以下每個類的一個實例。 即100個查詢創建下面的每個類的100個實例(具有DatastoreServiceConfig的異常,其每個查詢獲得2個實例)的 例外。這是GAE內存泄漏的證據嗎?

我在開發環境中使用Java VisualVM Profiler發現了這個問題。我做這個 的原因是,在生產中,我們的實例堆大小不斷增長(通常在10-20個請求後變得太大,因此最終導致響應緩慢和實例重新啓動)。 我不知道這是否是原因,但這是迄今爲止我已經能夠識別的第一個泄漏。

//泄漏與datanucleus.appengine.autoCreateDatastoreTxns =假

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 =真

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中真正的內存泄漏,還是僅僅是開發環境的事實,或者是我自己的錯誤?

回答

1

開發應用程序服務器旨在模擬真實應用程序服務器的語義,以便您可以以合理的保真度進行開發。這不包括內存行爲,特別是關於數據存儲。開發者服務器往往會以真實應用服務器不支持的方式將內容保存在內存中。對開發服務器進行性能分析對於排除應用程序端的泄漏仍然很有用,但不會爲應用服務器端的泄漏提供很多指導。不過,我們確實留意這些。隨着時間的推移,堆成碎片。

根據其使用性質和數據訪問模式,某些應用程序可從較大的前端實例中受益。那些花費更多,所以你必須測試和加重成本增加的好處。

+0

是的,我擔心生產與開發內存分析會是這樣的情況......是的,我們已經轉移到F2實例,因爲額外的內存幫助實例持續了一段時間,但內存泄漏似乎相當實質,所以它只是一個小的缺口。 – mrated

+0

我會繼續在我們自己的代碼中尋找泄漏,但是您是否有任何關於GAE實例堆增長如此之多的其他體驗的提示?我還沒有發現任何關於它的討論 - 難得的嗎? – mrated

0

它可能是GAE泄漏。它也可能是你的代碼中的一個錯誤。區分這些案件的唯一方法是追蹤(可能)泄漏的實際原因。

我應該補充一點,泄漏可能在你的代碼的另一部分;例如如果您在其他地方使用持久性管理器。 FWIW,您的問題中的代碼對我來說看起來還不錯...

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的使用情況,您可以調整RECYCLE_POINT。get(),但我發現5000個電話是獲得新實例的好點。

如果您收到任何PMF在使用時關閉的消息,BUFFER_ZONE也很重要。這意味着你正在保持一個PersistenceManagerFactory的句柄或做很長時間的請求。您應該在每次請求開始時(甚至在每次請求中多次)使用PMF.get()。如果您想要保留前一個PMF實例的時間更長但總是RECYCLE_POINT的一小部分,請增加BUFFER_ZONE。