簡體   English   中英

即使沒有GC根,類加載器也不會被垃圾收集

[英]Classloader is not garbage collected, even without GC roots

我們有一個在Glassfish V2.1.1下運行的復雜應用程序。 為了能夠動態加載我們的代碼,我們實現了一個CustomClassloader,它能夠重新定義類。 行為非常簡單:當動態加載的類發生更改時,CustomClassloader的當前實例將被“刪除”,並創建一個新實例以重新定義所需的類。

這很好用,除了在重新加載相同的類幾次之后(因此每次創建一個新的CustomClassloader),我們得到一個PermGen空間錯誤,因為CustomClassloader的其他實例不是垃圾收集的。 (這個類應該只有一個實例)

我嘗試了不同的方法來追蹤泄漏的位置:

  1. visualvm =>我進行堆轉儲並提取CustomClassloader的所有實例。 我可以看到他們都沒有最終確定。 當我檢查最近的GC根時,visualvm告訴我沒有(除了最后一個實例,因為它是'真實的'使用過的)。
  2. jmap / jhat =>它給了我幾乎相同的結果:我看到CustomClassloader的所有實例,然后當我點擊鏈接看看其中一個的引用在哪里時,我得到一個空白頁面,意思是沒有.. 。
  3. Eclipse Memory Analyzer Tool =>當我運行以下OQL查詢時,我得到一個奇怪的結果: 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):=

  1. 新的SAITaskClassLoader
  2. 加載數據D1,D2,...,Dn
  3. 新的SAITaskClassLoader
  4. 加載數據D1,D2,...,Dn
  5. ...

CL-GC

=模式2(類加載器未GCed):=

  1. 新的SAITaskClassLoader
  2. 加載數據D1,D2,D3
  3. 新的SAITaskClassLoader
  4. 加載數據D3,D4,D5
  5. 新的SAITaskClassLoader
  6. 加載數據D5,D6,D7
  7. ...

CL-nogc

在所有情況下,已清除的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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM