openjpa_OpenJPA:内存泄漏案例研究
openjpa
本文將提供完整的根本原因分析詳細信息以及解決影響Oracle Weblogic Server 10.0生產環境的Java堆內存泄漏(Apache OpenJPA泄漏)的方法。 這篇文章還將演示在管理javax.persistence.EntityManagerFactory生命周期時遵循Java Persistence API最佳實踐的重要性。
環境規格
- Java EE服務器 :Oracle Weblogic Portal 10.0
- 操作系統 :Solaris 10
- JDK :Oracle / Sun HotSpot JVM 1.5 32位@ 2 GB容量
- Java Persistence API :Apache OpenJPA 1.0.x(JPA 1.0規范)
- RDBMS :Oracle 10g
- 平臺類型 :Web門戶
故障排除工具
- Quest Foglight for Java( Java堆監視)
- MAT(Java 堆轉儲分析 )
問題描述與觀察
該問題最初是由Weblogic生產支持團隊在生產中斷后報告的。 最初的根本原因分析練習確實揭示了以下事實和觀察結果:
- 約2周的流量后,定期觀察到生產中斷。
- 失敗是由于Java堆(OldGen)耗盡所致,例如OutOfMemoryError:在Weblogic日志中發現Java堆空間錯誤。
- 在一段時間后從Foglight監視工具檢查了Java堆OldGen的空間利用率以及Java冗長的GC歷史數據后,確認了Java堆內存泄漏。
發現上述問題后,決定移至RCA的下一個階段,并對受影響的Weblogic(JVM)實例執行JVM 堆轉儲分析 。
JVM堆轉儲分析
**現在可以在此處獲得解釋以下JVM堆轉儲分析的視頻。 為了產生一個
JVM堆轉儲受支持的團隊確實使用了HotSpot 1.5 jmap實用程序,該實用程序生成了大約1.5 GB的堆轉儲文件(heap.bin)。 然后使用Eclipse Memory Analyzer Tool分析了堆轉儲文件。 現在,讓我們回顧一下堆轉儲分析,以便我們了解OldGen內存泄漏的根源。
MAT提供了一個初步的泄漏可疑報告,這對于突出您的高內存貢獻者非常有用。 對于我們的問題案例,MAT能夠識別出可疑泄漏占近600 MB或占OldGen空間總容量的40%。
此時,我們找到了一個java.util.LinkedList實例,該實例使用了將近600 MB的內存,并已加載到我們的應用程序父類加載器之一(@ 0x7e12b708)。 下一步是了解泄漏的對象以及保留的來源。 MAT允許您檢查應用程序的任何類加載器實例,并提供檢查加載的類和實例的功能。 只需提供地址(例如0x7e12b708)來搜索所需的對象,然后通過選擇帶有外向引用的“列表對象”>檢查加載的類和實例。
從上面的快照中您可以看到,該分析非常有啟發性。 我們發現內存保留源中有一個org.apache.openjpa.enhance.PCRegistry實例; 罪魁禍首是實現為LinkedList的_listeners字段。 供您參考,內部使用Apache OpenJPA PCRegistry來跟蹤已注冊的具有持久性的類。 在Apache OpenJPA 1.0.4版中公開_listeners字段的PCRegistry源代碼的片段下面。
/*** Tracks registered persistence-capable classes.** @since 0.4.0* @author Abe White*/ public class PCRegistry {// DO NOT ADD ADDITIONAL DEPENDENCIES TO THIS CLASSprivate static final Localizer _loc = Localizer.forPackage(PCRegistry.class);// map of pc classes to meta structs; weak so the VM can GC classesprivate static final Map _metas = new ConcurrentReferenceHashMap(ReferenceMap.WEAK, ReferenceMap.HARD);// register class listenersprivate static final Collection _listeners = new LinkedList();現在的問題是,為什么這種內部數據結構的內存占用空間如此之大,并且隨著時間的推移可能會泄漏? 下一步是深入研究_listeners LinkedLink實例,以檢查泄漏的對象。
我們最終發現,泄漏的對象實際上是我們的應用程序用來對Oracle數據庫執行各種查詢的JDBC和SQL映射定義(元數據)。 對JPA規范,OpenJPA文檔和源代碼的回顧確實確認了根本原因與javax.persistence.EntityManagerFactory的錯誤用法有關,例如缺少關閉新創建的EntityManagerFactory實例。
如果仔細查看上面的代碼快照,您將意識到close()方法確實負責清理最近使用的元數據存儲庫實例。 這也確實引起了另一個問題,為什么我們要一遍又一遍地創建這樣的Factory實例……調查的下一步是對我們的應用程序代碼執行代碼遍歷,尤其是圍繞JPA EntityManagerFactory和EntityManager對象的生命周期管理。
根本原因和解決方案
應用程序代碼的代碼演練確實表明,該應用程序在每個單個請求上都創建了EntityManagerFactory的新實例,并且沒有正確關閉它。
public class Application {@Resourceprivate UserTransaction utx = null;// Initialized on each application request and not closed!@PersistenceUnit(unitName = "UnitName")private EntityManagerFactory emf = Persistence.createEntityManagerFactory("PersistenceUnit"); public EntityManager getEntityManager() {return this.emf.createEntityManager();}public void businessMethod() {// Create a new EntityManager instance via from the newly created EntityManagerFactory instance// Do something...// Close the EntityManager instance} }JPA EntityManagerFactory的此代碼缺陷和改進程序的使用導致在早期JVM堆轉儲分析中演示的OpenJPA _listeners數據結構內泄漏或積累了元數據存儲庫實例。 該問題的解決方案是通過Singleton模式集中管理線程安全javax.persistence.EntityManagerFactory的管理和生命周期。 最終解決方案的實現如下:
- 每個應用程序類加載器僅創建和維護一個javax.persistence.EntityManagerFactory靜態實例,并通過Singleton Pattern實現。
- 為每個應用程序請求創建并處置EntityManager的新實例。
請閱讀Stackoverflow上的 討論 ,因為我們實現的解決方案非常相似。 在針對我們的生產環境實施解決方案之后,不再觀察到Java堆OldGen內存泄漏。
參考: OpenJPA:我們的JCG合作伙伴 Pierre-Hugues Charbonneau在Java EE支持模式和Java教程博客上的內存泄漏案例研究 。
翻譯自: https://www.javacodegeeks.com/2013/03/openjpa-memory-leak-case-study.html
openjpa
總結
以上是生活随笔為你收集整理的openjpa_OpenJPA:内存泄漏案例研究的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 笔记本电脑如何卸载360卫士(电脑的36
- 下一篇: gradle web_简单的Gradle