JavaOne Tokyo 2012 JS2-14 マルチコア CPU 時代の Java プログラミング 2012 年 4 月 5 日 富士通株式会社数村憲治 Copyright 2012 FUJITSU LIMITED
自己紹介 数村憲治 kzr@jp.fujitsu.com 富士通株式会社 Interstage Application Server 開発チーム Java VMの開発 サポート 大規模システムでの性能チューニングに 数多く携わる 1 Copyright 2012 FUJITSU LIMITED
アジェンダ ハードウェアの進化と性能問題 メモリ関連問題 ロック関連問題 まとめ Q&A 2 Copyright 2012 FUJITSU LIMITED
アジェンダ ハードウェアの進化と性能問題 いまどきのCPU コア数を意識するJava よくあるパターン メモリ関連問題 ロック関連問題 まとめ Q&A 3 Copyright 2012 FUJITSU LIMITED
いまどきの CPU Xeon E7-8870 コア数 10 スレッド数 20 SPARC T3 コア数 16 スレッド数 128 PRIMEQUEST 1800E2 CPU 数 8 コア数 80 スレッド数 160 SPARC T3-4 CPU 数 4 コア数 64 スレッド数 512 マルチコアからメニーコアへ 4 Copyright 2012 FUJITSU LIMITED
コア数を意識する Java HotSpot VM のエルゴノミクス機能 Java VM が動作するマシン情報から自動判定 GC のワーカースレッド数に影響 確認方法 ( スレッドダンプ ) "GC task thread#0 (ParallelGC)" prio=10 tid=0x088d1800 nid=0x3428 runnable "GC task thread#1 (ParallelGC)" prio=10 tid=0x088d3000 nid=0x3429 runnable "GC task thread#2 (ParallelGC)" prio=10 tid=0x088d4400 nid=0x3430 runnable "GC task thread#3 (ParallelGC)" prio=10 tid=0x088d5000 nid=0x3431 runnable "GC task thread#4 (ParallelGC)" prio=10 tid=0x088d6000 nid=0x3432 runnable -XX:ParallelGCThreads=n ( パラレル GC 使用時 ) -XX:ConcGCThreads=n ( コンカレント GC 使用時 ) 5 Copyright 2012 FUJITSU LIMITED
コア数を意識する Java java.util. concurrent.forkjoinpool タスクを分割し work-stealingモデルで並列に実行 デフォルトの並列数は 論理 CPU 数 ForkJoinPool pool = new ForkJoinPool(); pool.invoke(sometask); ForkJoinPool pool = new ForkJoinPool(properValue); pool.invoke(sometask); 6 Copyright 2012 FUJITSU LIMITED
よくあるパターン CPU のマルチコア スレッド化により論理 CPU 数の増加 ハードの性能が上がった と喜ぶ アプリケーションで同時に実行するスレッド数を増加させる 同時実行スレッドが少ない時には発生しなかった問題が浮上 7 Copyright 2012 FUJITSU LIMITED
アジェンダ ハードウェアの進化と性能問題 メモリ関連問題 メモリアロケーション ガベージコレクション ロック関連問題 まとめ Q&A 8 Copyright 2012 FUJITSU LIMITED
メモリアロケーション Java のアロケーションはロックレス TLAB(Thread Local Alloc Buffer) Eden 領域 ( 初期状態 ) Eden 領域 ( アロケート時 ) スレッド A に割当て スレッド B に割当て スレッド A からのメモリ要求 スレッド B からのメモリ要求 多重度を上げても問題ないように見えるが 9 Copyright 2012 FUJITSU LIMITED
メモリアロケーション スレッド数が多いと TLAB が非効率に Eden 領域 スレッド 1 用 スレッド 2 用 スレッド 3 用 スレッド 4 用 スレッド (N+1) 用の TLAB が割当てられない スレッド 5 用 使用域 スレッド N 用 未使用域 Eden 全体では 未使用域がたくさんあるが GC が発生 10 Copyright 2012 FUJITSU LIMITED
ガベージコレクション JNI 使用時には注意が必要 cstr = (*env)->getstringcritical(env, string, &iscopy); ~ 長い処理 ~ (*env)->releasestringcritical(env, string, cstr); GetStringCritical ReleaseStringCritical 間は ガベージコレクションが抑止 他のスレッドで OutOfMemoryError の可能性 GetPrimitiveArrayCritical も同様 11 Copyright 2012 FUJITSU LIMITED
アジェンダ ハードウェアの進化と性能問題 メモリ関連問題 ロック関連問題 JavaVMロック機構の進化 無意識のロック フェアネスロック 性能分析 まとめ Q&A 12 Copyright 2012 FUJITSU LIMITED
JavaVM ロック機構の進化 ほとんどのロックは共有すらされていない 第三世代バイアスロック ロード命令を使用 ほとんどのロックは競合していない 第二世代シンロック CAS 命令を使用 マルチコア CPU では CAS のコストが増大 第一世代ヘビーロック OS の mutex 関数を使用 オブジェクト毎に mutex を用意 ロック機構の進化はロック競合時の改良ではない 13 Copyright 2012 FUJITSU LIMITED
無意識のロック API ドキュメントには どのメソッドが synchronize メソッドか 記述なし 内部的に synchronized ブロックを使用している場合もあり java.util.hashtable java.lang.stringbuffer java.lang.string#getbytes() java.net.inetaddress#getallbyname() java.io.file#renameto() 14 Copyright 2012 FUJITSU LIMITED
無意識のロック java.util.hashtable クラス ほとんどのメソッドが synchronize メソッド スレッドローカルで使用するなら java.util.hashmap 等を java.lang.stringbuffer クラス ほとんどのメソッドが synchronize メソッド java.lang.stringbuilder の使用を 15 Copyright 2012 FUJITSU LIMITED
無意識のロック java.lang.string#getbytes() byte[] b1 = str1.getbytes( MS932 ); byte[] b2 = str2.getbytes( EUC_JP ); byte[] b3 = str3.getbytes( UTF-8 ); at sun.nio.cs.fastcharsetprovider.charsetforname(fastcharsetprovi -waiting to lock <0x74b1e5d8> (a sun.nio.cs.standardcharsets) at java.nio.charset.charset.lookup2(charset.java:487) at java.nio.charset.charset.lookup(charset.java:475) at java.nio.charset.charset.issupported(charset.java:517) at java.lang.stringcoding.lookupcharset(stringcoding.java:99) at java.lang.stringcoding.encode(stringcoding.java:335) at java.lang.string.getbytes(string.java:955) 16 Copyright 2012 FUJITSU LIMITED
無意識のロック java.net.inetaddress#getallbyname() InetAddress ia = InetAddress.getAllByName( hostname ); at java.net.inetaddress.getcachedaddresses(inetaddress.java:839) -waiting to lock <0x74b40e78> (a java.net.inetaddress$cache) at java.net.inetaddress.getallbyname0(inetaddress.java:1207) at java.net.inetaddress.getallbyname(inetaddress.java:1127) at java.net.inetaddress.getallbyname(inetaddress.java:1063) 17 Copyright 2012 FUJITSU LIMITED
無意識のロック java.io.file#renameto() (delete/mkdirs/getcanonicalpath も同様 ) File f1 = new File( ); File f2 = new File( ); f1.renameto(f2); at java.io.expiringcache.clear(expiringcache.java:98) -locked <0x9fa0c328> (a java.io.expiringcache) at java.io.unixfilesystem.rename(unixfilesystem.java:276) at java.io.file.renameto(file.java:1314) 18 Copyright 2012 FUJITSU LIMITED
フェアネスロック lock = new Object(); public void run() { for (int i = 0 ; i < 100 ; i++) { synchronized (lock) { System.out.println( i=" + i + " tid=" + Thread.currentThread().getId()); } } 期待する結果 i=0 tid=9 i=0 tid=12 i=0 tid=11 i=1 tid=9 i=0 tid=10 i=0 tid=14 i=1 tid=10 実際の結果 i=0 tid=9 i=1 tid=9 i=2 tid=9 i=3 tid=9 i=4 tid=9 i=5 tid=9 i=6 tid=9 19 Copyright 2012 FUJITSU LIMITED
フェアネスロック ロック待ち ロック中 レスポンス重視 スレッドA スレッドB スレッドC スレッドD ロック解放ロック解放ロック解放ロック解放 各スレッドでロック待ち時間が均等 スループット重視 スレッドA スレッドB スレッドC スレッドD ロック解放ロック解放ロック解放ロック解放 飛びぬけてロック待ち時間の長いスレッドが存在 20 Copyright 2012 FUJITSU LIMITED
モニター synchronized (o) { 待合室 オーナー部屋 1 Monitor Enter Monitor 2 Entered 3 待ちスレッド オーナースレッド Monitor Exit 1: Monitor Enter: 待合室に入る この段階ではロックは取れていない 2: Monitor Entered: オーナー部屋に入る この段階でロック獲得 3: Monitor Exit: 部屋から退出 ロックを解放 21 Copyright 2012 FUJITSU LIMITED
フェアネスロック synchronize(d) は フェアネスロックではない フェアネスを期待する場合は java.util.concurrent.locks.reentrantlock lock = new ReentrantLock(true); // true: フェアネスの指定 public void run() { for (int i = 0 ; i < 100 ; i++) { try { lock.lock(); System.out.println( i=" + i + " tid=" + Thread.currentThread().getId()); } finally { lock.unlock(); } } 22 Copyright 2012 FUJITSU LIMITED
性能分析 System.out#println() による区間分析 long time1 = System.currentTimeMillis(); System.out.println( 区間 A 開始 : + threadid + : + time1); n = n+1; long time2 = System.currentTimeMillis(); System.out.println( 区間 A 終了 : + threadid + : + time2); 実行結果 区間 A 開始 :9:11000 区間 A 開始 :8:11010 区間 A 終了 :8:11020 区間 A 開始 :7:20010 区間 A 終了 :7:20020 区間 A 終了 :9:21000 スレッド 9 だけ 10 秒かかっている System.out#pintln() 内でも synchronized の使用 23 Copyright 2012 FUJITSU LIMITED
性能分析 JVMTI の例 void JNICALL cbmethodenter(jvmtienv *env, ) { メソッド名の抽出 (*env)->rawmonitorenter( ); メソッド名のログ書き込み (*env)->rawmonitorexit( ); void JNICALL cbmethodenter(jvmtienv *env, ) { メソッド名の抽出 (*env)->getthreadlocalstorage(env, thread, &tls); tls へメソッド名の記録 24 Copyright 2012 FUJITSU LIMITED
アジェンダ ハードウェアの進化と性能問題 メモリ関連問題 ロック関連問題 まとめ Q&A 25 Copyright 2012 FUJITSU LIMITED
まとめ ハードウェアの更改時は要注意 プログラム変更が必要な場合も わずかのロックが命取りに できるだけロックを使わない 無意識に使っているロックを見極める ロックポリシーの選択 レスポンス重視かスループット重視か デバッグコードが性能ネックに 正しい性能測定を 26 Copyright 2012 FUJITSU LIMITED
Q&A 27 Copyright 2012 FUJITSU LIMITED
28 Copyright 2011 FUJITSU LIMITED
29 Copyright 2011 FUJITSU LIMITED