$30 off During Our Annual Pro Sale. View Details »

マルチコアCPU時代のJavaプログラミング

 マルチコアCPU時代のJavaプログラミング

JavaOne Tokyo 2012

Kenji Kazumura

May 23, 2022
Tweet

More Decks by Kenji Kazumura

Other Decks in Programming

Transcript

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  6. 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
    ・・・・
    確認方法(スレッドダンプ)

    View Slide

  7. 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);

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  15. 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()
    ・・・

    View Slide

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

    View Slide

  17. 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”);

    View Slide

  18. 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”);

    View Slide

  19. 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);

    View Slide

  20. 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
    ・・・
    期待する結果 実際の結果

    View Slide

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

    View Slide

  22. 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

    View Slide

  23. 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

    View Slide

  24. 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の使用

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  28. 27 Copyright 2012 FUJITSU LIMITED
    Q&A

    View Slide

  29. 28 Copyright 2011 FUJITSU LIMITED

    View Slide

  30. 29 Copyright 2011 FUJITSU LIMITED

    View Slide