classloader leak patterns
DESCRIPTION
TRANSCRIPT
クラスローダリークパターン
なにそれおいしいの?
Takayoshi Kimura
Senior Software Maintenance Engineer, JBossGlobal Support Service
Red Hat
自己紹介
nekop
● JBossの中の人● Emacsで Java書く人●オープンソース大好きっ子●ネコよりイヌが好き
宣伝
JBossではエンジニアを募集しております!
JBossで働きませんか● Javaとオープンソースが好きな人におすすめ
●おやつ無料●飲み物自動販売機無料
JBossで働きませんか●主に「オープンソースで遊ぶ」のと「技術的な人助け」
● Linuxカーネルから Javaアーキテクトまで豊富な同僚
本題
クラスローダリーク
何が起こる?● java.lang.OutOfMemoryError: perm gen space
●クラス定義情報が置いてあるメモリ領域 (Perm)を消費しっぱなし
いつリークする?●クラスローダが破棄、再作成されるタイミング
●具体例として、アプリケーションサーバに再デプロイを行ったとき
なぜリークする?●そのクラスローダへの参照が残ってるから
●そのクラスローダからロードされたクラスへの参照が残ってるから
●クラスローダをまたがる参照
パターンその1
ThreadLocal
ThreadLocal
●同一スレッド上で値の受け渡しができて便利
●フレームワークなどでよく使われている
ThreadLocalの参照構造● ThreadLocalに設定した値はThreadから強参照される
●詳しく知りたい人はソース嫁
ThreadLocal開放条件● ThreadLocalへ到達可能な強参照が一つもない場合開放される
● Threadが開放されたら一緒に開放される
ありがちなリークコードpublic class ThreadLocalKeyValue {
private ThreadLocal tl = new ThreadLocal();
public void add(Object key, Object value) { tl.set(new KeyValuePair(key, value)); }
public Object getKey() { return ((KeyValuePair)tl.get()).key; }
public Object getValue() { return ((KeyValuePair)tl.get()).value; }
private class KeyValuePair { public Object key, value; public KeyValuePair(Object key, Object value) { this.key = key; this.value = value; } }}
テストThreadLocalKeyValue target = new ThreadLocalKeyValue();ReferenceQueue queue = new ReferenceQueue();PhantomReference ref = new PhantomReference(target, queue);
target.add("foo", "bar");target = null;
System.gc();System.out.println("Released?: " + ref.isEnqueued());
テスト結果falseって何よ
( ゚Д ゚ )
修正public class ThreadLocalKeyValue {
private ThreadLocal tl = new ThreadLocal();
public void add(Object key, Object value) { tl.set(new KeyValuePair(key, value)); }
public Object getKey() { return ((KeyValuePair)tl.get()).key; }
public Object getValue() { return ((KeyValuePair)tl.get()).value; }
private static class KeyValuePair { public Object key, value; public KeyValuePair(Object key, Object value) { this.key = key; this.value = value; } }}
テスト結果true
ハァ ?( ゚Д ゚ )
ThreadLocalまとめ● 設定した値は Threadから強参照される● ThreadLocalに渡す「値」の参照関係に注意● スレッドのコントロールが自分に無い環境では
Threadの開放に伴う ThreadLocalの開放は期待できない– アプリケーションサーバ上では当然スレッドはプールされている
– リークしないのは「入れたら確実に消す (一時的な利用 )」か「入れっぱなしでも値の参照関係が安全だと保証できる」場合のみ
ThreadLocalと Tomcat
● Tomcatはアンデプロイ時に ThreadLocalをリフレクションで開放してくれる機能がある– アプリケーションやフレームワーク、ライブラリのクラスローダリークバグを隠してしまういやんな機能
– 心当たりのあるフレームワーク /ライブラリ開発者さんは直してください!
● でもその機能にスレッドセーフじゃないバグがあるので 6.0.27以降は無効になっている– https://issues.apache.org/bugzilla/show_bug.
cgi?id=48895
パターンその2
java.util.logging
java.util.loggingの Level
● Level継承するとクラスローダがリークする
●ここ嫁● http://blogs.sun.com/fkieviet/entry/cl
assloader_leaks_the_dreaded_java
パターンその 3既成
ライブラリ
既成ライブラリ● 元々複数のクラスローダ上で動作することを想定していないライブラリ– ContextClassLoaderや staticフィールドの誤用– ThreadLocalやWeakHashMapなどの参照構造の理解不足
既成ライブラリ● Commons-logging
– だいぶ前に一通り直ったけど、利用する側のコードによってはリークする
● Commons-beanutils– 1.8.0時点で一応メジャーな問題は全部解決している
– https://issues.apache.org/jira/browse/BEANUTILS-291
● 他にもいっぱいあるよ!