2012-03-05 68 views
6

我在哪裏工作,我們正在經歷與JVM在我們的應用運行的堆空間不足的問題。我已經做了一些尋找這個原因的方法,包括用剖析器來查看堆轉儲,但現在我幾乎陷入了困境。休眠,SessionFactoryObjectFactory和OutOfMemoryError異常:Java堆空間

首先,介紹一下系統中的問題:它是一種Java應用程序,使用Spring和Hibernate,保存有關的組織。該系統由一組web服務客戶端組成,用於從負責此類數據的政府機構檢索有關組織的數據。此外,系統還使用這些數據保存本地數據庫,作爲Web服務調用的緩存,以便首次請求有關組織的信息,並將其保存在本地關係數據庫中,並將其用於檢索的數據用於以下請求。 Hibernate用於與此數據庫進行通信。

的問題,如前面所指出的,是在一段時間後,該應用程序開始的OutOfMemoryError崩潰:Java堆空間。我已經使用Eclipse + MAT查看了堆轉儲,並將問題確定爲Hibernate的SessionFactoryObjectFactory,佔用分配內存的大約85%(全部保留了內存)。我發現有點難以確切地確定這種類型的對象。在頂層有Glassfish WebappClassLoader,它包含org.hibernate.impl.SessionFactoryObjectFactory。包含在這裏的是一個org.hibernate.util.FastHashMap,它又包含一個java.util.HashMap。這包含許多條目,每個條目都包含一個HashMap條目,一個org.hibernate.impl.SessionFactoryImpl和一個String。 HashMap-entry反過來包含相同的三個對象,一個HashMap-entry,一個SessionFactoryImpl和一個String,並且這個結構重複了很多次。 SessionFactoryImpls包含許多對象,其中最着名的是org.hibernate.persister.entity.SingleTableEntityPersister,它包含許多Strings和HashMaps。一些字符串是指域對象中的變量,有些則包含sql語句。

乍一看,這個對象佔用了大量內存(轉儲文件是800MB,其中650MB被SessionFactoryObjectFactory佔用),所以我啓用了對象加載和卸載記錄,並試圖詢問系統提供有關組織的數據(通過來自另一個系統的Web服務調用)。我在這裏注意到的是,有很多關於加載對象的消息,但是關於卸載的對象(只有那些卸載庫對象的對象)很少。這使我相信,一旦一個對象(比如組織)被加載到內存中,它就永遠不會被卸載,這意味着隨着時間的推移,系統將耗盡內存。 (這是基於什麼在日誌中發現了一個合理的假設?)

於是,我試圖找到這個原因,但這是困難得多。作爲由Hibernate加載的對象將生活,只要他們的會話的生活,我試圖改變會議分別處理的方式,通過更換Spring的HibernateDaoSupport的#的getSession(電話),以#的​​HibernateDaoSupport了getSessionFactory()的getCurrentSession()。這對問題沒有影響。我還嘗試在一些Dao方法的finally塊中添加對... getCurrentSession()。flush()和.clear()的調用,也沒有出現任何外觀效果。 (該道的方法都標註有@Transactional,這應該意味着一個會議只應@事務法中活着,並調用的getCurrentSession(當該方法的連續調用應該得到不同的會話)(?))

所以,現在我很困難,當涉及到其他地區檢查。有沒有人有一個想法或一些指示關於去哪裏尋找和尋找什麼?

堆轉儲顯示有很多org.hibernate.impl.SessionFactoryImpl的實例,這是否如預期的那樣? (我會認爲應該只有一個SessionFactory的實例,或幾個上衣。)

在此先感謝您的任何答案。

問候,

TOBB

編輯:

我覺得我其實mananged來解決這個問題:

原來的方式依賴於其他對象在被處理Web服務類是問題。這是通過在webservice類的構造函數中調用新的ClassPathXmlApplicationContext(...)來解決的。這導致爲每個請求(或至少每個會話)加載大量對象,這些對象不會再次被卸載(主要是Hibernate的SessionFactoryImpl)。我已經改變了webservice-classes,所以他們注入了他們的依賴關係,並且形成了迄今爲止我所見過的使用探查器的東西,多個SessionFactoryImpl對象的問題已經解決。

我認爲從GlassFish 2.x升級到GlassFish 3.x可能會導致問題惡化,可能與實例化web服務類有些不同。

回答

5

我可能也只是在問題本身的解決方案添加到這個問題的答案,而不是:

這裏的罪魁禍首是如何彈簧豆裝載在各種物體做,尤其是在Web服務類。這是通過呼叫完成的

new ClassPathXmlApplicationContext(...)

在個人web服務類。這樣做有加載對象的令人討厭的副作用,避免垃圾收集(我猜是因爲它們被Spring內部的一些引用)。看起來glassfish版本的改變對webservice-objects的實例化有了一些作用,導致對更多新的調用的調用,從而導致更多的垃圾對象佔用內存,直到它填滿和崩潰。

的解決問題的方法是把呼叫轉移到

new ClassPathXmlApplicationContext(...)

出分成不同的類,使用靜態工廠模式,是這樣的:

public class ContextHolder { 
    private static ClassPathXmlApplicationContext context; 

    public static getSpringContext() { 
     if (context == null) { 
      context = new ClassPathXmlApplicationContext("applicationContext.xml"); 
     } 
     return context; 
    } 
} 

而且在調用此web服務類,而不是新的ClassPathXmlApplicationContext。

更新:

ClassPathXmlApplicationContextCloseable/Autocloseable,所以try-with-resource是另一種可能性:

try (final ClassPathXmlApplicationContext applicationContext = 
      new ClassPathXmlApplicationContext("applicationContext.xml")) { 
    //do stuff 
} 
+0

或者靜態工廠,我想'ClassPathXmlApplicationContext'有一個'close'-方法,其可呼籲避免這個問題。 – Tobb 2014-09-15 07:32:29