性能計測の実行
やっと環境がほぼ整いました。
それではJmeterでCentOSに負荷をかけて、サーバーで実行されるKernelの情報を取得しましょう。
以下の準備が整っている状態で実行します。
・サーバーが起動していること
・perf-map-agentがアタッチされていること
・Jmeterが設定されていること。
perf-map-agentの設定はこれから行います。
①perf-map-agentをアタッチ
Linuxでjavaを実行した際には、実行時のアドレスとメソッド名の対応が読み取れないんですね。読み取ることができるようにするためにperf-map-agentというツールを使用します。
[root@localhost ~]# chmod 777 ~
[root@localhost ~]# mkdir -m 777 work
[root@localhost ~]# ll | grep work
drwxrwxrwx. 2 root root 6 9月 15 21:26 work
[root@localhost ~]# cd work
[root@localhost work]# git clone https://github.com/jrudolph/perf-map-agent.git
Cloning into 'perf-map-agent'...
remote: Enumerating objects: 438, done.
remote: Total 438 (delta 0), reused 0 (delta 0), pack-reused 438
Receiving objects: 100% (438/438), 127.74 KiB | 0 bytes/s, done.
Resolving deltas: 100% (208/208), done.
perf-map-agentのサイトを見ると、cmakeコマンドを利用して、ビルドを行うよう書かれています。cmakeコマンドのインストールが必要になります。
ただし、cmakeコマンドを実行するために、cおよびc++のコンパイラが必要になりますので、こちらも事前にインストールしておきます。
c++のコンパイラであるg++のパッケージ、「gcc-c++」をインストールします。
このパッケージのインストールで、cのコンパイラであるgccも依存性解決のため、一緒にインストールされます。
[root@localhost perf-map-agent]# yum install gcc-c++
:
依存性関連をインストールしました:
cpp.x86_64 0:4.8.5-39.el7 gcc.x86_64 0:4.8.5-39.el7
glibc-devel.x86_64 0:2.17-307.el7.1 glibc-headers.x86_64 0:2.17-307.el7.1
kernel-headers.x86_64 0:3.10.0-1127.19.1.el7 libstdc++-devel.x86_64 0:4.8.5-39.el7
完了しました!
c、c++のコンパイラをインストールできたら、cmakeをインストールします。
[root@localhost work]# yum install cmake
んで、さっそくperf-map-agentをビルドします。
[root@localhost perf-map-agent]# cmake .
-- The C compiler identification is GNU 4.8.5
-- The CXX compiler identification is GNU 4.8.5
-- Check for working C compiler: /usr/bin/cc
-- Check for working C compiler: /usr/bin/cc -- works
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Check for working CXX compiler: /usr/bin/c++
-- Check for working CXX compiler: /usr/bin/c++ -- works
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- JNI_INCLUDE_DIRS=/usr/lib/jvm/java/include;/usr/lib/jvm/java/include/linux;/usr/lib/jvm/java/include
-- JNI_LIBRARIES=/usr/lib/jvm/jre/lib/amd64/libjawt.so;/usr/lib/jvm/jre/lib/amd64/server/libjvm.so
-- JAVA_INCLUDE_PATH=/usr/lib/jvm/java/include
-- JAVA_INCLUDE_PATH2=/usr/lib/jvm/java/include/linux
-- Configuring done
-- Generating done
-- Build files have been written to: /root/work/perf-map-agent
[root@localhost perf-map-agent]# make
Scanning dependencies of target attach-main
[ 20%] Building Java objects for attach-main.jar
[ 40%] Generating out/CMakeFiles/attach-main.dir/java_class_filelist
[ 60%] Creating Java archive attach-main.jar
[ 60%] Built target attach-main
Scanning dependencies of target perfmap
[ 80%] Building C object CMakeFiles/perfmap.dir/src/c/perf-map-agent.c.o
[100%] Building C object CMakeFiles/perfmap.dir/src/c/perf-map-file.c.o
Linking C shared library out/libperfmap.so
[100%] Built target perfmap
ビルドできたらglassfishのプロセスにアタッチします。
[root@localhost perf-map-agent]# ps -u glassfish
PID TTY TIME CMD
3207 pts/1 00:00:00 bash
3271 pts/1 00:00:30 java
## glassfishで実行しているGlassgishのPIDは3271なので、、、
[root@localhost perf-map-agent]# cd bin
[root@localhost bin]# ./create-java-perf-map.sh 3271
②FlameGraphの取得
NetflixのBrendan Greggさんが作成した、perfコマンドのプロファイルを可視化してくれるツールを取得します。
## 自分のホーム配下のworkディレクトリへ移動
[root@localhost bin]# cd ~/work
## ディレクトリの状態を確認
[root@localhost work]# ll
合計 0
drwxr-xr-x. 5 root root 59 9月 15 23:52 JavaPerformanceTuning
drwxr-xr-x. 7 root root 225 9月 15 23:50 perf-map-agent
## FlameGraphをGitで取得
[root@localhost work]# git clone https://github.com/brendangregg/FlameGraph.git
Cloning into 'FlameGraph'...
remote: Enumerating objects: 1, done.
remote: Counting objects: 100% (1/1), done.
remote: Total 1067 (delta 0), reused 0 (delta 0), pack-reused 1066
Receiving objects: 100% (1067/1067), 1.87 MiB | 1.62 MiB/s, done.
Resolving deltas: 100% (612/612), done.
## ダウンロード結果を確認
[root@localhost work]# ll
合計 4
drwxr-xr-x. 7 root root 4096 9月 28 15:09 FlameGraph
drwxr-xr-x. 5 root root 59 9月 15 23:52 JavaPerformanceTuning
drwxr-xr-x. 7 root root 225 9月 15 23:50 perf-map-agent
## ダウンロードしたFlameGraphのディレクトリに移動
[root@localhost work]# cd FlameGraph
[root@localhost FlameGraph]#
この状態で以降の作業を行います。
③Jmeterの実行
Javaの性能計測するうえで、Jmeterのボタンを押したところでの計測はまだちょっと早いです。計測するにあたってウォームアップを行う必要があります。
Javaはコンパイラ型の言語ですが、実行時にすべてのコードをコンパイルするのではなく、実行に必要なコードだけをコンパイルする仕様になっています。これはJITコンパイラと呼ばれていますが、Java起動直後は最適なコンパイル状態となるまで、何回か命令を実行する必要があり、それがいわゆるウォームアップと呼ばれるものです。
先ほどのJmeterのGraph Resultsで見ると下記のようなグラフになっているのはそのためです。

今回はJmeterのThread Groupの設定を以下のようにします。
サーバーによっては負荷が高すぎてハングアップし、レスポンスが返ってこなくなってしまうので、必要に応じて下げたり上げたりするといいでしょう。

今回は残りの60秒での状態をプロファイリング(測定)します。
Jmeterで実行開始して、60秒が経過したらサーバー側で以下の通りコマンドを実行します。
[root@localhost FlameGraph]# perf record -F 99 -p 17509 -g -- sleep 60
## 60秒経過すると以下の通り出力されます。
[ perf record: Woken up 6 times to write data ]
[ perf record: Captured and wrote 1.985 MB perf.data (2705 samples) ]
## perf.dataができていることの確認
[root@localhost FlameGraph]# ll | grep perf.data
-rw-------. 1 root root 2092048 9月 28 15:18 perf.data
その後、取得したFlameGraphで実行結果を作成します。
[root@localhost FlameGraph]# perf script | ./stackcollapse-perf.pl > out.perf-folded
[root@localhost FlameGraph]# ./flamegraph.pl out.perf-folded > perf-kernel.svg
出来上がったSVGファイルをブラウザで開いてみましょう。

各セクションをクリックすると、そこから上部のスタック部分を詳細に確認することができます。
グラフを簡単に説明すると、真ん中部分が主にリクエストを処理している部分です。インタプリタが多くでてきているあたり、チューニングがまだまだ足りないみたいですね。
右端では、G1GCのコンパイラが動いている様子が示されています。
リクエスト処理をしながらコンパイラが仕事しているものですね。
上部を見ると、javaからkernelへ命令が遷移している様子が見えますね。
データ送信にかかわる箇所では以下のように、Javaのユーザー空間からkernel空間での実行の順番が見えます。

こちらを見ながらカーネル周りの処理内容を見ていくことによってチューニングを実施していくことになります。
これでLinux Kernelの世界に入門できるのではないでしょうか。
Linux Kernelに入門したければJavaを使わず直接C言語でごちゃごちゃやっちゃう手もあるんですけどね。
次回の技術記事はスクレイピングとかfirebaseについて書いてみましょうかね。
CentOS7とJavaの性能検証用環境の構築