Webapp執行reload後內存泄漏之SSLSocketFactory
tomcat/jetty是怎麼做reload的呢?
首先, webapp的WEB-INF/lib目錄並不在jvm的classpath內, javaee容器(tomcat/jetty/jboss)是通過自定義的ClassLoader來載入它們的.
而這個ClassLoader,通常的名字就叫做WebappClassLoader, 容器會為每個webapp的每次啟動,都創建一個新的ClassLoader.
「每個webapp」,保證了不同webapp之間的類隔離, 例如有A/B兩個webapp,都使用了DEF類的XXX靜態屬性,那麼在JVM裡面就有2份DEF類,兩份XXX靜態屬性.
「每次啟動」, 是因為容器會先執行一次unload,再執行load,相當於一個新的webapp載入進來.
為什麼reload有泄漏?
首先,什麼是泄漏? 就是你創建了某些對象/數據, 期望它會被GC, 但事實上沒有.
那為啥不被GC呢? 那肯定是被引用了.
虛無的ROOT --> 根ClassLoader --> 一些類的靜態屬性 --> 對象(webapp裡面創建的對象) --> 對象的類 --> WebappClassLoader --> 類 --> 靜態屬性 虛無的ROOT --> 線程組 --> 線程 --> 對象(webapp裡面創建的對象) --> 對象的類 --> WebappClassLoader --> 其他類 --> 靜態屬性
兩條路徑:
webapp內創建的對象,賦值到了根ClassLoader載入的一個類的靜態屬性上.
webapp內創建的線程,reload之後也沒有stop
一時半刻想不出其他路徑了 -_-
靜態屬性的實例
一個非常非常經典的寫法, 忽略Https的無效證書(通常是自簽名證書)
SSLContext sc = SSLContext.getInstance("SSL"); TrustManager[] tmArr = }; sc.init(null, tmArr, new SecureRandom()); HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
HttpsURLConnection並非WebappClassLoader載入,而是由根ClassLoader載入.
然後, new X509TrustManager(){}所創建的匿名內部類對象的類,是由WebappClassLoader載入的.
所以呢, 上述代碼就把一個 WebappClassLoader所載入的類的實例,賦值給根ClassLoader載入的類的一個靜態屬性.
最後, 當Webapp被reload時, 老的WebappClassLoader不會被GC, 直至上述代碼再被執行,屬性值被覆蓋.
在一年前, nutz的Http也是這個寫法, 後來改成HttpsURLConnection的實例方法setSSLSocketFactory
類似的代碼存在於很多需要訪問第三方網站的java庫裡面, 例如jpush, socialauth
線程創建導致的泄漏
這種就只能靠自律了… 例如dubbo裡面就建立一堆線程池,然而沒有提供銷毀的方法…


※提升生活品質的良品
※均衡之道—ThinkPad T470 上手體驗
※講真,如今的好設計還是離不開數據的支持
※小米筆記本 Air 13.3 指紋版上手:新增了指紋識別,圖形處理器能力也更強
※SA9527:最好的開始,永遠是現在
TAG:推酷 |
※C#開發Android應用之WebApp
※使用C#開發Android應用之WebApp
※前端工程師的新選擇?聊聊WebApp的未來
※給你一個webAPP,應該如何運行起來?