Diese Präsentation wurde erfolgreich gemeldet.
Wir verwenden Ihre LinkedIn Profilangaben und Informationen zu Ihren Aktivitäten, um Anzeigen zu personalisieren und Ihnen relevantere Inhalte anzuzeigen. Sie können Ihre Anzeigeneinstellungen jederzeit ändern.
Tomcatの実装から学ぶ
ClassLoaderLeak
@n-agetsu
上妻 宜人 (あげつま のりと)
第十回 #渋谷java
第十回 #渋谷java
あげつま のりと
• SIer勤務
• Javaトラブルシューティング
• JBoss, Tomcat 社内サポート
• はてな 見習いプログラミング日記
• Software Design 2014 10月号 寄稿
第十回 #渋谷java
ClassLoaderLeakって?
第十回 #渋谷java
ホットデプロイ時に古いクラスローダが
GCされない困った不具合です
(〜JDK7) java.lang.OutOfMemoryError : Perm Gen
(JDK8〜) Metaspaceの増大/OutOfMemo...
第十回 #渋谷java
ホットデプロイの普及により
遭遇率があがっています
OOM!
.war
deploy
deploy
deploy
.war
第十回 #渋谷javahttp://upload.wikimedia.org/wikipedia/commons/thumb/7/73/Edit-clear_mirrored.svg/120px-Edit-clear_mirrored.svg....
第十回 #渋谷java
Tomcat リーク検知実装コードを読む
(org.apache.catalina.loader.WebappClassLoaderBase)
何をするとリークするかわかる
第十回 #渋谷java
Common
クラスローダ
Webapp
クラスローダ
( 稼働中WAR)
アンデプロイ済
Webapp
クラスローダ
ClassLoaderLeakの主な原因
Bootstrap
クラスローダ
System
クラスロー...
第十回 #渋谷java
Common
クラスローダ
Webapp
クラスローダ
( 稼働中WAR)
アンデプロイ済
Webapp
クラスローダ
ClassLoaderLeakの主な原因
Bootstrap
クラスローダ
System
クラスロー...
第十回 #渋谷java
Tomcatが検知するClassLoaderLeak
• java.lang.ThreadLocal.remove 漏れ
• java.sql.DriverManager.deregisterDriver 漏れ
• その...
第十回 #渋谷java
ThreadLocal.remove 実行漏れ
public class UserThreadContext {
private static ThreadLocal<User> context = ...;
publi...
第十回 #渋谷java
ThreadLocal.remove 実行漏れ
public class UserThreadContext {
private static ThreadLocal<User> context = ...;
publi...
第十回 #渋谷java
なぜremove漏れでリークする?
ThreadLocalに
ユーザ情報を追加
HTTPリクエスト
Tomcatスレッドプール
ThreadLocalMap
Entry
User
プールに戻ったスレッドが持つThread...
第十回 #渋谷java
なぜremove漏れでリークする?
ThreadLocalに
ユーザ情報を追加
HTTPリクエスト
Tomcatスレッドプール
ThreadLocalMap
Entry
UserTomcatの場合、アンデプロイ時に
プー...
第十回 #渋谷java
DriverManager.deregisterDriver 漏れ
.war のWEB-INF/libにJDBCドライバを含めて以下コードで再現
@WebServlet(“/leak”)
public class Lea...
第十回 #渋谷java
JDBCクラスロード時にDriverManagerに登録
import org.postgresql;
public class Driver implements java.sql.Driver {
static {
...
第十回 #渋谷java
DriverManagerによるリークの対処
• JDBCドライバを.warに含めない
• ServletContextListenerでAP終了時に解放
• java.sql.DriverManager.deregis...
第十回 #渋谷java
どうやってClassLoaderLeakを
解析するか?
第十回 #渋谷java
ClassLoaderLeakの見つけ方
1. HeapDump取得
jcmd <pid> GC.heapdump filename=...
2. Eclipse Memory Analyzer
ロードしたら “Dupl...
第十回 #渋谷java
ClassLoaderLeakの見つけ方
同じクラスが複数のWebappClassLoaderから
ロードされていればClassLoaderLeakの可能性大
第十回 #渋谷java
Path To GC Roots で原因を特定
TomcatのTaskThreadが持つThreadLocalからの参照が原因
GCして欲しいWebappClassLoaderを
選択してGCルートをチェック
第十回 #渋谷java
ライブラリによるClassLoaderLeak
• 伝統的なライブラリにあるClassLoaderLeak
• Commons Logging 1.0.4 以前
• iBATIS 2.3.4 以前 (iBATIS-540...
第十回 #渋谷java
ClassLoaderLeakはTomcatだけではない
• WebLogicServer や WildFly でも起こり得る
(JVM起動中に動的にクラスローダの生成・破棄があれば起こる可能性有)
• 個人的にはWeb...
第十回 #渋谷java
まとめ
• ClassLoaderLeakはホットデプロイにより顕在化
• Tomcatは色々なClassLoaderLeakの検知・解放が可能
• 原因はThreadLocal解放漏れを筆頭に多種多様
• ClassL...
Nächste SlideShare
Wird geladen in …5
×

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

17.399 Aufrufe

Veröffentlicht am

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

Veröffentlicht in: Software
  • Als Erste(r) kommentieren

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

  1. 1. Tomcatの実装から学ぶ ClassLoaderLeak @n-agetsu 上妻 宜人 (あげつま のりと) 第十回 #渋谷java
  2. 2. 第十回 #渋谷java あげつま のりと • SIer勤務 • Javaトラブルシューティング • JBoss, Tomcat 社内サポート • はてな 見習いプログラミング日記 • Software Design 2014 10月号 寄稿
  3. 3. 第十回 #渋谷java ClassLoaderLeakって?
  4. 4. 第十回 #渋谷java ホットデプロイ時に古いクラスローダが GCされない困った不具合です (〜JDK7) java.lang.OutOfMemoryError : Perm Gen (JDK8〜) Metaspaceの増大/OutOfMemoryError (-XX:MaxMetaSpaceSize設定時)
  5. 5. 第十回 #渋谷java ホットデプロイの普及により 遭遇率があがっています OOM! .war deploy deploy deploy .war
  6. 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. 7. 第十回 #渋谷java Tomcat リーク検知実装コードを読む (org.apache.catalina.loader.WebappClassLoaderBase) 何をするとリークするかわかる
  8. 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. 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. 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. 11. 第十回 #渋谷java ThreadLocal.remove 実行漏れ public class UserThreadContext { private static ThreadLocal<User> context = ...; public static void setUser(User user) { userContext.set(user); } } 掃除されてないスレッドローカル
  12. 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. 13. 第十回 #渋谷java なぜremove漏れでリークする? ThreadLocalに ユーザ情報を追加 HTTPリクエスト Tomcatスレッドプール ThreadLocalMap Entry User プールに戻ったスレッドが持つThreadLocalMapから アプリケーションへの参照が残り続ける ThreadLocalMap Entry User
  14. 14. 第十回 #渋谷java なぜremove漏れでリークする? ThreadLocalに ユーザ情報を追加 HTTPリクエスト Tomcatスレッドプール ThreadLocalMap Entry UserTomcatの場合、アンデプロイ時に プールのチェック・リフレッシュにより、 ThreadLocalによるリークを検知・解放 解放: ThreadPoolExecutor.setCorePoolSize(0); (プースサイズを0にしてidleを全て解放)
  15. 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. 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. 17. 第十回 #渋谷java DriverManagerによるリークの対処 • JDBCドライバを.warに含めない • ServletContextListenerでAP終了時に解放 • java.sql.DriverManager.deregisterDriver実行 • Tomcat8ではデフォルトで検知・解放が有効 • JREMemoryLeakPreventionListenerで解放される
  18. 18. 第十回 #渋谷java どうやってClassLoaderLeakを 解析するか?
  19. 19. 第十回 #渋谷java ClassLoaderLeakの見つけ方 1. HeapDump取得 jcmd <pid> GC.heapdump filename=... 2. Eclipse Memory Analyzer ロードしたら “Duplicate Classes”
  20. 20. 第十回 #渋谷java ClassLoaderLeakの見つけ方 同じクラスが複数のWebappClassLoaderから ロードされていればClassLoaderLeakの可能性大
  21. 21. 第十回 #渋谷java Path To GC Roots で原因を特定 TomcatのTaskThreadが持つThreadLocalからの参照が原因 GCして欲しいWebappClassLoaderを 選択してGCルートをチェック
  22. 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. 23. 第十回 #渋谷java ClassLoaderLeakはTomcatだけではない • WebLogicServer や WildFly でも起こり得る (JVM起動中に動的にクラスローダの生成・破棄があれば起こる可能性有) • 個人的にはWebLogicServerで何度か遭遇 • プロダクション再デプロイメントの使用を意図 • WebLogicは悪くない, 前述の AP or ライブラリ起因でリーク
  24. 24. 第十回 #渋谷java まとめ • ClassLoaderLeakはホットデプロイにより顕在化 • Tomcatは色々なClassLoaderLeakの検知・解放が可能 • 原因はThreadLocal解放漏れを筆頭に多種多様 • ClassLoaderLeakは怖くない • ヒープダンプより比較的簡単に原因特定が可能 • Duplicate Class => Path to GC Roots

×