Tomcat如何檢測內存泄漏
一般情況下,如果我們重啟web應用是通過重啟tomcat的話,則不存在內存泄漏問題。但如果不重啟tomcat而對web應用進行重加載則可能會導致內存泄漏,因為重加載后有可能會導致原來的某些內存無法讓GC回收,例如web應用使用了JDBC,驅動會進行注冊,當web應用停止時沒有反注冊就會導致內存泄漏。
看看是什么原因導致tomcat內存泄漏的。這個要從熱部署開始說起,因為tomcat提供了不必重啟容器而只需重啟web應用以達到熱部署的功能,其實現是通過定義一個WebappClassLoader類加載器,當熱部署時就將原來的類加載器廢棄并重新實例化一個WebappClassLoader類加載器。但這種方式可能存在內存泄漏問題,因為ClassLoader是一個結構復雜的對象,導致它不能被GC回收的可能性比較多,除了對ClassLoader對象有引用可能導致其無法回收,還可能對其加載的元數據(方法、類、字段等)有引用都會導致無法被GC回收。
如上圖,tomcat的資源由不同類加載器加載,這里只看BootstrapClassLoader和WebappClassLoader兩個類加載器,BootstrapClassLoader負責加載rt.jar包的Java.sql.DriverManager,WebappClassLoader負責加載web應用中的MySQL驅動包,其中有一個很重要的步驟是mysql的驅動類需要注冊到DriverManager中,即DriverManager.registerDriver(new Driver()),它由mysql驅動包自動完成。這樣一來當web應用進行熱部署操作時,沒有將mysql的Driver從DriverManager中反注冊掉的話,則會導致整個WebappClassLoader回收不了,造成內存泄漏。
接著看tomcat如何對此內存泄漏進行監控的,要判斷WebappClassLoader會不會導致內存泄漏只需判斷WebappClassLoader有沒有被GC回收即可。在Java中有一種引用叫弱引用,它能很好判斷WebappClassLoader有沒有被GC回收,被弱引用關聯的對象只能生存到下一次垃圾回收發生之前,即如果某WebappClassLoader對象只被某弱引用關聯,則它會在下次垃圾回收時被回收,但如果WebappClassLoader對象除了被弱引用關聯外還被其他對象強引用,那么WebappClassLoader對象是不會被回收的,根據這些條件就可以判斷是否有WebappClassLoader內存泄漏了。
Tomcat的實現是通過WeakHashMap來實現弱引用的,只需將WebappClassLoader對象put到WeakHashMap中,例如weakMap.put(“a”,webappClassLoader),當webappClassLoader及其包含的元素沒有被其它任何類加載器中的元素引用到時,JVM發生垃圾回收時則會把webappClassLoader對象回收。
簡單的實現代碼大致如下:
public class MemoryLeakTest{private Map<ClassLoader, String> childClassLoaders = new WeakHashMap<ClassLoader, String>();public String[] findReloadedContextMemoryLeaks() { System.gc(); List<String> result = new ArrayList<String>(); for (Map.Entry<ClassLoader, String> entry : childClassLoaders.entrySet()) { ClassLoader cl = entry.getKey(); if (!((WebappClassLoader) cl).isStarted()) { result.add(entry.getValue()); } } return result.toArray(new String[result.size()]); }}
使用一個WeakHashMap用于跟蹤WebappClassLoader,在查找內存泄漏之前會先強制調用System.gc();進行一次垃圾回收,保證沒問題的WebappClassLoader都被回收掉,這時如果還有WebappClassLoader的狀態是非started(正常啟動的都為started,關閉了的則為非started)的,則是未被垃圾回收的WebappClassLoader,屬于內存泄漏的。
感謝閱讀,希望能幫助到大家,謝謝大家對本站的支持!
新聞熱點
疑難解答
圖片精選