[英]Classloader is not garbage collected, even without GC roots
我們有一個在Glassfish V2.1.1下運行的復雜應用程序。 為了能夠動態加載我們的代碼,我們實現了一個CustomClassloader,它能夠重新定義類。 行為非常簡單:當動態加載的類發生更改時,CustomClassloader的當前實例將被“刪除”,並創建一個新實例以重新定義所需的類。
這很好用,除了在重新加載相同的類幾次之后(因此每次創建一個新的CustomClassloader),我們得到一個PermGen空間錯誤,因為CustomClassloader的其他實例不是垃圾收集的。 (這個類應該只有一個實例)
我嘗試了不同的方法來追蹤泄漏的位置:
SELECT c FROM INSTANCEOF my.package.CustomClassloader c
只有一個結果,表明只有一個實例顯然不正確。 我還檢查了這個鏈接,並在創建一個新的CustomClassloader時實現了一些資源釋放,但沒有任何變化:PermGen內存仍在增加。
所以我可能錯過了一些東西,點(1-2)和(3)之間的區別顯示了我不理解的東西。 我在哪里可以了解什么是錯的? 由於我所遵循的所有教程都顯示了如何使用“搜索最近的GC根”功能搜索泄漏的引用(在我的情況下沒有),我不知道如何跟蹤錯誤。
編輯1:我在這里上傳了一個堆轉儲示例。 可以使用以下查詢select s from saierp.core.framework.system.SAITaskClassLoader s
未卸載的ClassLoader: select s from saierp.core.framework.system.SAITaskClassLoader s
可以看到有4個實例,並且應該已經收集了三個實例,因為沒有GC根......某處必須有參考,但我不知道如何搜索它。 任何提示都歡迎:)
編輯2:經過一些更深入的測試,我看到一個非常奇怪的模式。 泄漏似乎取決於OpenJPA正在加載的數據:如果沒有加載新數據,那么類加載器可以是GCed,否則不是。 下面是我創建一個新的SAITaskClassLoader以“清除”舊代碼時使用的代碼:
PCRegistry.deRegister(cl);
LogFactory.release(cl);
ResourceBundle.clearCache(cl);
Introspector.flushCaches();
=模式1(類加載器已GCed):=
=模式2(類加載器未GCed):=
在所有情況下,已清除的SAITaskClassLoader都沒有GC根。 我們正在使用OpenJPA 1.2.1。
謝謝和最好的問候
如果沒有CustomClassLoader
的源代碼CustomClassLoader
或實際的堆轉儲,則很難找到問題所在。 您的CustomClassLoader
不能是單例。 如果是,你的設計無法工作(或者我錯過了什么)。
您需要獲取CustomClassLoader
類型的ClassLoader
實例列表,並跟蹤對這些對象的引用。
這些帖子可以幫助您進一步分析它,並進入暗中追捕ClassLoader泄漏的暗示:
類加載器的垃圾收集是一項非常棘手的業務。 使用JProfiler ,我看到以下對當前活動的自定義類加載器的傳入引用鏈:
這表明您的自定義類加載器中有一個靜態字段“singleInstance”,它引用了類加載器本身。 您應該嘗試在重新部署時清除該字段,以便VM更容易收集類加載器。
關於Eclipse MAT獲得的結果的注釋:它刪除了所有不可訪問的對象。 JProfiler默認也這樣做。 因此,三個先前的類加載器應該被垃圾收集,但它們不是,因為JVM對於類加載器GC具有特殊規則,而這些規則未被堆中的標准引用捕獲。
免責聲明:我公司開發JProfiler
最后,我可以關閉這個bug,因為它似乎與OpenJPA和非參數化查詢相關聯。 另一個要看的線程: 自定義ClassLoader沒有垃圾回收
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.