Your SlideShare is downloading. ×
Tomcatの実装から学ぶクラスローダリーク #渋谷Java
Upcoming SlideShare
Loading in...5
×

Thanks for flagging this SlideShare!

Oops! An error has occurred.

×

Introducing the official SlideShare app

Stunning, full-screen experience for iPhone and Android

Text the download link to your phone

Standard text messaging rates apply

Tomcatの実装から学ぶクラスローダリーク #渋谷Java

189
views

Published on

第十回 渋谷JavaのLTスライドです。

第十回 渋谷JavaのLTスライドです。

Published in: Software

0 Comments
1 Like
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total Views
189
On Slideshare
0
From Embeds
0
Number of Embeds
0
Actions
Shares
0
Downloads
0
Comments
0
Likes
1
Embeds 0
No embeds

Report content
Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
No notes for slide

Transcript

  • 1. Tomcatの実装から学ぶ ClassLoaderLeak @n-agetsu 上妻 宜人 (あげつま のりと) 第十回 #渋谷java
  • 2. 第十回 #渋谷java あげつま のりと • SIer勤務 • Javaトラブルシューティング • JBoss, Tomcat 社内サポート • はてな 見習いプログラミング日記 • Software Design 2014 10月号 寄稿
  • 3. 第十回 #渋谷java ClassLoaderLeakって?
  • 4. 第十回 #渋谷java ホットデプロイ時に古いクラスローダが GCされない困った不具合です (〜JDK7) java.lang.OutOfMemoryError : Perm Gen (JDK8〜) Metaspaceの増大/OutOfMemoryError (-XX:MaxMetaSpaceSize設定時)
  • 5. 第十回 #渋谷java ホットデプロイの普及により 遭遇率があがっています OOM! .war deploy deploy deploy .war
  • 6. 第十回 #渋谷javahttp://upload.wikimedia.org/wikipedia/commons/thumb/7/73/Edit-clear_mirrored.svg/120px-Edit-clear_mirrored.svg.png 【NEW】 test.war 【OLD】 test.war TomcatはClassLoaderLeakの 検知・解放機能を実装しています。 参考: O’Reilly Japan 詳解Tomcat
  • 7. 第十回 #渋谷java Tomcat リーク検知実装コードを読む (org.apache.catalina.loader.WebappClassLoaderBase) 何をするとリークするかわかる
  • 8. 第十回 #渋谷java Common クラスローダ Webapp クラスローダ ( 稼働中WAR) アンデプロイ済 Webapp クラスローダ ClassLoaderLeakの主な原因 Bootstrap クラスローダ System クラスローダ Tomcat スレッドプール 1. 上位クラスローダで読み込まれた クラスからの強参照 例 : java.sql.DriverManagerのフィールド変数 ⇒ WEB-INF/libのJDBCドライバ など 2. プール内スレッドからの参照 ・ スタック中スレッドが旧APクラスを参照 ・ ThredLocal.remove() の漏れ Tomcat8のデフォルトの クラスローダ階層 (クラスローダについては以下参照) org.apache.catalina.startup.Bootstrap.initメソッド と org.apache.catalina.loader.WebappLoaderクラス
  • 9. 第十回 #渋谷java Common クラスローダ Webapp クラスローダ ( 稼働中WAR) アンデプロイ済 Webapp クラスローダ ClassLoaderLeakの主な原因 Bootstrap クラスローダ System クラスローダ Tomcat スレッドプール 1. 上位クラスローダで読み込まれた クラスからの強参照 例 : java.sql.DriverManagerのフィールド変数 ⇒ WEB-INF/libのJDBCドライバ など 2. プール内スレッドからの参照 ・ スタック中スレッドが旧APクラスを参照 ・ ThredLocal.remove() の漏れ Tomcat8のデフォルトの クラスローダ階層 (クラスローダについては以下参照) org.apache.catalina.startup.Bootstrap.initメソッド と org.apache.catalina.loader.WebappLoaderクラス JDKクラス、Tomcatスレッドプール など アンデプロイしても使われるクラスから、 アンデプロイ済APへの参照が残るのが共通点
  • 10. 第十回 #渋谷java Tomcatが検知するClassLoaderLeak • java.lang.ThreadLocal.remove 漏れ • java.sql.DriverManager.deregisterDriver 漏れ • その他 • RMI関連 sun.rmi.transport.ObjectTable のobjTable, implTable クリア漏れ • 実行中スレッドによる参照 • java.beans.Introspector.flushCaches() の実行漏れ • Commons HttpClient の keep alive 用スレッドが残り続ける • java.util.Timer.cancel() キャンセル漏れ • 古いJVM? のGCバグ static / final 変数解放漏れ (JDK7, 8では再現せず) org.apache.catalina.loader.WebappClassLoaderBase クラスを読んでみると..
  • 11. 第十回 #渋谷java ThreadLocal.remove 実行漏れ public class UserThreadContext { private static ThreadLocal<User> context = ...; public static void setUser(User user) { userContext.set(user); } } 掃除されてないスレッドローカル
  • 12. 第十回 #渋谷java ThreadLocal.remove 実行漏れ public class UserThreadContext { private static ThreadLocal<User> context = ...; public static void setUser(User user) { userContext.set(user); } // filterとかinterceptorとかでリクエスト終了までに実行 public static void cleanup() { userContext.remove(); } } データは入れたら消す。cleanupコードの追加。
  • 13. 第十回 #渋谷java なぜremove漏れでリークする? ThreadLocalに ユーザ情報を追加 HTTPリクエスト Tomcatスレッドプール ThreadLocalMap Entry User プールに戻ったスレッドが持つThreadLocalMapから アプリケーションへの参照が残り続ける ThreadLocalMap Entry User
  • 14. 第十回 #渋谷java なぜremove漏れでリークする? ThreadLocalに ユーザ情報を追加 HTTPリクエスト Tomcatスレッドプール ThreadLocalMap Entry UserTomcatの場合、アンデプロイ時に プールのチェック・リフレッシュにより、 ThreadLocalによるリークを検知・解放 解放: ThreadPoolExecutor.setCorePoolSize(0); (プースサイズを0にしてidleを全て解放)
  • 15. 第十回 #渋谷java DriverManager.deregisterDriver 漏れ .war のWEB-INF/libにJDBCドライバを含めて以下コードで再現 @WebServlet(“/leak”) public class LeakServlet extends HttpServlet { @Override public void doGet(...) { try { Class.forName(“org.postgresql.Driver”); } catch (ClassNot.. e) { ... } } }
  • 16. 第十回 #渋谷java JDBCクラスロード時にDriverManagerに登録 import org.postgresql; public class Driver implements java.sql.Driver { static { try { java.sql.DriverManager.registerDriver(new Driver()); .... Common Webapp Undeployed WEB-INF/lib/xxxJDBC.jar Bootstrap System DriverManager • .war にJDBCに含めた場合、参照が残る。 • tomcat/libに置いた場合は、ドライバが Commonでクラスロードされるので残らない。
  • 17. 第十回 #渋谷java DriverManagerによるリークの対処 • JDBCドライバを.warに含めない • ServletContextListenerでAP終了時に解放 • java.sql.DriverManager.deregisterDriver実行 • Tomcat8ではデフォルトで検知・解放が有効 • JREMemoryLeakPreventionListenerで解放される
  • 18. 第十回 #渋谷java どうやってClassLoaderLeakを 解析するか?
  • 19. 第十回 #渋谷java ClassLoaderLeakの見つけ方 1. HeapDump取得 jcmd <pid> GC.heapdump filename=... 2. Eclipse Memory Analyzer ロードしたら “Duplicate Classes”
  • 20. 第十回 #渋谷java ClassLoaderLeakの見つけ方 同じクラスが複数のWebappClassLoaderから ロードされていればClassLoaderLeakの可能性大
  • 21. 第十回 #渋谷java Path To GC Roots で原因を特定 TomcatのTaskThreadが持つThreadLocalからの参照が原因 GCして欲しいWebappClassLoaderを 選択してGCルートをチェック
  • 22. 第十回 #渋谷java ライブラリによるClassLoaderLeak • 伝統的なライブラリにあるClassLoaderLeak • Commons Logging 1.0.4 以前 • iBATIS 2.3.4 以前 (iBATIS-540) • log4j 1.2.16 以前 (Bug 50486, MDC利用時のみ) • その他 • ClassLoader, ThreadLocal, HotDeploy Leak で検索
  • 23. 第十回 #渋谷java ClassLoaderLeakはTomcatだけではない • WebLogicServer や WildFly でも起こり得る (JVM起動中に動的にクラスローダの生成・破棄があれば起こる可能性有) • 個人的にはWebLogicServerで何度か遭遇 • プロダクション再デプロイメントの使用を意図 • WebLogicは悪くない, 前述の AP or ライブラリ起因でリーク
  • 24. 第十回 #渋谷java まとめ • ClassLoaderLeakはホットデプロイにより顕在化 • Tomcatは色々なClassLoaderLeakの検知・解放が可能 • 原因はThreadLocal解放漏れを筆頭に多種多様 • ClassLoaderLeakは怖くない • ヒープダンプより比較的簡単に原因特定が可能 • Duplicate Class => Path to GC Roots