深入jvm類卸載機(jī)制:強(qiáng)引用鏈與類加載器回收
本文深入探討JVM的類卸載機(jī)制,重點(diǎn)分析類加載器與類之間的相互引用關(guān)系,并解答MyCounter.class和WebAppClassLoader是否會(huì)因循環(huán)引用導(dǎo)致內(nèi)存泄漏的問題。
文中舉例說明了強(qiáng)引用鏈:線程 -> ThreadLocalMap -> counter -> MyCounter.class -> WebAppClassLoader,導(dǎo)致WebAppClassLoader無法回收。 這引發(fā)了MyCounter.class卸載條件的疑問。 文章指出,類卸載的必要條件之一是加載該類的類加載器被回收。 這引出了核心問題:如果MyCounter.class由WebAppClassLoader加載,MyCounter.class是否會(huì)反過來強(qiáng)引用WebAppClassLoader,從而阻止其回收,最終導(dǎo)致MyCounter.class也無法卸載?
關(guān)鍵在于理解“無任何外部引用”的含義。 認(rèn)為WebAppClassLoader回收的唯一條件是“沒有任何引用”是不準(zhǔn)確的。 即使存在MyCounter.class和WebAppClassLoader之間的循環(huán)引用,如果它們沒有被其他對(duì)象(例如,程序中的其他類、線程等)直接或間接引用,JVM的垃圾回收器(例如標(biāo)記清除算法)仍然可以識(shí)別并回收它們。 因?yàn)閺腉C Root(例如線程棧)出發(fā),無法訪問這兩個(gè)對(duì)象。 因此,即使存在循環(huán)引用,只要沒有其他強(qiáng)引用指向它們,它們?nèi)匀粫?huì)被GC回收。
所以,MyCounter.class并不一定總是強(qiáng)引用WebAppClassLoader,阻止其回收。 知乎文章中提到的內(nèi)存泄漏,更可能是由于線程未正確結(jié)束,導(dǎo)致ThreadLocal中持有的對(duì)象無法回收,最終導(dǎo)致WebAppClassLoader及其加載的類無法卸載。 問題的根源不在于類與類加載器的相互引用,而在于程序沒有正確管理線程和資源,造成了內(nèi)存泄漏。