Slide 1

Slide 1 text

Copyright 2012 FUJITSU LIMITED JS2-14 マルチコアCPU時代の Javaプログラミング 2012年4月5日 富士通株式会社 数村 憲治 JavaOne Tokyo 2012

Slide 2

Slide 2 text

1 Copyright 2012 FUJITSU LIMITED  数村 憲治 [email protected] 富士通株式会社  Interstage Application Server開発チーム  Java VMの開発・サポート  大規模システムでの性能チューニングに、 数多く携わる 自己紹介

Slide 3

Slide 3 text

2 Copyright 2012 FUJITSU LIMITED  ハードウェアの進化と性能問題  メモリ関連問題  ロック関連問題  まとめ  Q&A アジェンダ

Slide 4

Slide 4 text

3 Copyright 2012 FUJITSU LIMITED  ハードウェアの進化と性能問題 いまどきのCPU コア数を意識するJava よくあるパターン  メモリ関連問題  ロック関連問題  まとめ  Q&A アジェンダ

Slide 5

Slide 5 text

4 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 マルチコアからメニーコアへ

Slide 6

Slide 6 text

5 Copyright 2012 FUJITSU LIMITED  HotSpot VMのエルゴノミクス機能 Java VMが動作するマシン情報から自動判定 GCのワーカースレッド数に影響 コア数を意識するJava -XX:ParallelGCThreads=n (パラレルGC使用時) -XX:ConcGCThreads=n (コンカレント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 ・・・・ 確認方法(スレッドダンプ)

Slide 7

Slide 7 text

6 Copyright 2012 FUJITSU LIMITED  java.util. concurrent.ForkJoinPool タスクを分割し、work-stealingモデルで並列に実行。 デフォルトの並列数は、論理CPU数。 コア数を意識するJava ForkJoinPool pool = new ForkJoinPool(); pool.invoke(someTask); ForkJoinPool pool = new ForkJoinPool(properValue); pool.invoke(someTask);

Slide 8

Slide 8 text

7 Copyright 2012 FUJITSU LIMITED よくあるパターン CPUのマルチコア・スレッド化により論理CPU数の増加 ハードの性能が上がった、と喜ぶ アプリケーションで同時に実行するスレッド数を増加させる 同時実行スレッドが少ない時には発生しなかった問題が浮上

Slide 9

Slide 9 text

8 Copyright 2012 FUJITSU LIMITED  ハードウェアの進化と性能問題  メモリ関連問題 メモリアロケーション ガベージコレクション  ロック関連問題  まとめ  Q&A アジェンダ

Slide 10

Slide 10 text

9 Copyright 2012 FUJITSU LIMITED  Javaのアロケーションはロックレス  TLAB(Thread Local Alloc Buffer) メモリアロケーション Eden領域(初期状態) スレッドAに割当て スレッドBに割当て スレッドAからの メモリ要求 スレッドBからの メモリ要求 Eden領域(アロケート時) 多重度を上げても問題ないように見えるが。。。

Slide 11

Slide 11 text

10 Copyright 2012 FUJITSU LIMITED  スレッド数が多いとTLABが非効率に メモリアロケーション Eden領域 スレッド1用 スレッド2用 スレッド4用 スレッド3用 スレッド5用 スレッドN用 使用域 未使用域 スレッド(N+1)用のTLABが 割当てられない Eden全体では、 未使用域がたくさんあるが、 GCが発生

Slide 12

Slide 12 text

11 Copyright 2012 FUJITSU LIMITED  JNI使用時には注意が必要 ガベージコレクション cstr = (*env)->GetStringCritical(env, string, &isCopy); ~ 長い処理 ~ (*env)->ReleaseStringCritical(env, string, cstr); GetStringCritical  ReleaseStringCritical間は、 ガベージコレクションが抑止。 他のスレッドでOutOfMemoryErrorの可能性。 GetPrimitiveArrayCriticalも同様。

Slide 13

Slide 13 text

12 Copyright 2012 FUJITSU LIMITED  ハードウェアの進化と性能問題  メモリ関連問題  ロック関連問題 JavaVMロック機構の進化 無意識のロック フェアネスロック 性能分析  まとめ  Q&A アジェンダ

Slide 14

Slide 14 text

13 Copyright 2012 FUJITSU LIMITED JavaVMロック機構の進化 ・OSのmutex関数を使用 ・オブジェクト毎にmutexを用意 ・ロード命令を 使用 ロック機構の進化はロック競合時の改良ではない ・CAS命令を使用 ・マルチコアCPUでは CASのコストが増大 ほとんどのロックは 競合していない ほとんどのロックは 共有すらされていない 第一世代 ヘビーロック 第二世代 シンロック 第三世代 バイアスロック

Slide 15

Slide 15 text

14 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() ・・・

Slide 16

Slide 16 text

15 Copyright 2012 FUJITSU LIMITED  java.util.Hashtableクラス ほとんどのメソッドがsynchronizeメソッド スレッドローカルで使用するなら、 java.util.HashMap等を。  java.lang.StringBufferクラス ほとんどのメソッドがsynchronizeメソッド java.lang.StringBuilderの使用を。 無意識のロック

Slide 17

Slide 17 text

16 Copyright 2012 FUJITSU LIMITED  java.lang.String#getBytes() 無意識のロック 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) byte[] b1 = str1.getBytes(“MS932”); byte[] b2 = str2.getBytes(“EUC_JP”); byte[] b3 = str3.getBytes(“UTF-8”);

Slide 18

Slide 18 text

17 Copyright 2012 FUJITSU LIMITED  java.net.InetAddress#getAllByName() 無意識のロック 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) InetAddress ia = InetAddress.getAllByName(“hostname”);

Slide 19

Slide 19 text

18 Copyright 2012 FUJITSU LIMITED  java.io.File#renameTo() (delete/mkdirs/getCanonicalPathも同様) 無意識のロック 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) File f1 = new File(“・・・”); File f2 = new File(“・・・”); f1.renameTo(f2);

Slide 20

Slide 20 text

19 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=1 tid=9 i=2 tid=9 i=3 tid=9 i=4 tid=9 i=5 tid=9 i=6 tid=9 ・・・ 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 ・・・ 期待する結果 実際の結果

Slide 21

Slide 21 text

20 Copyright 2012 FUJITSU LIMITED フェアネスロック スループット重視 ロック解放 ロック解放 ロック解放 ロック解放 レスポンス重視 ロック解放 ロック解放 ロック解放 ロック解放 ロック待ち ロック中 スレッドA スレッドB スレッドC スレッドD スレッドA スレッドB スレッドC スレッドD 各スレッドで ロック待ち時間が均等 飛びぬけて ロック待ち時間の長い スレッドが存在

Slide 22

Slide 22 text

21 Copyright 2012 FUJITSU LIMITED モニター 待合室 オーナー部屋 Monitor Exit Monitor Entered Monitor Enter 待ちスレッド オーナースレッド 1: Monitor Enter: 待合室に入る。この段階ではロックは取れていない。 synchronized (o) { ・・・ 2: Monitor Entered: オーナー部屋に入る。この段階でロック獲得。 3: Monitor Exit: 部屋から退出。ロックを解放。 1 2 3

Slide 23

Slide 23 text

22 Copyright 2012 FUJITSU LIMITED  synchronize(d)は、フェアネスロックではない フェアネスロック 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(); } }  フェアネスを期待する場合は、 java.util.concurrent.locks.ReentrantLock

Slide 24

Slide 24 text

23 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の使用

Slide 25

Slide 25 text

24 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へメソッド名の記録

Slide 26

Slide 26 text

25 Copyright 2012 FUJITSU LIMITED  ハードウェアの進化と性能問題  メモリ関連問題  ロック関連問題  まとめ  Q&A アジェンダ

Slide 27

Slide 27 text

26 Copyright 2012 FUJITSU LIMITED  ハードウェアの更改時は要注意 プログラム変更が必要な場合も  わずかのロックが命取りに できるだけロックを使わない 無意識に使っているロックを見極める  ロックポリシーの選択 レスポンス重視かスループット重視か  デバッグコードが性能ネックに 正しい性能測定を まとめ

Slide 28

Slide 28 text

27 Copyright 2012 FUJITSU LIMITED Q&A

Slide 29

Slide 29 text

28 Copyright 2011 FUJITSU LIMITED

Slide 30

Slide 30 text

29 Copyright 2011 FUJITSU LIMITED