More Related Content Similar to Serviceability Toolsの裏側 Similar to Serviceability Toolsの裏側 (20) More from Yasumasa Suenaga More from Yasumasa Suenaga (7) Serviceability Toolsの裏側2. 自己紹介
末永 恭正 @YaSuenag
某通信キャリアでJavaの障害解析して
ますした
hs_errorログやコアの解析 などなど
ネイティブ寄りなJavaプログラマ
基本サンデープログラマー
PascalとかCとかJavaとか…
すえなが やすまさ
3. Serviceability Tools ?
Serviceability in the J2SE Repository
http://openjdk.java.net/groups/serviceability/svcjdk.html
jconsole
jdb
jhat
jinfo
jmap
jps
jcontrol
jsadebugd
jstack
jstat
jstatd
6. Dynamic Attach
ツールからの要求に応じてHotSpotが情報収集
各ツールとHotSpotの連係プレー
ツール側
○ sun.tools.attach.HotSpotVirtualMachineの
提供するメソッドを実行
○ 専用のコマンドと引数をターゲットVMへ送信
HotSpot側
○ Attach Listenerが指定された処理を実行し、結果を
ツールへ返却
"Attach Listener" daemon prio=10 tid=0x00007f92dc001000 nid=0x261e runnable [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
9. AttachListenerのコマンド
コマンド 機能 利用ツール
agentProperties エージェント用システムプロパ
ティの取得
• jdb
• jconsole
• JMXエージェント
datadump SIGQUIT送出(と等価) なし
dumpheap ヒープダンプ生成 jmap
load JVMTIエージェントのロード jconsole
properties システムプロパティの取得 • jconsole
• jinfo
threaddump スレッドダンプの取得 jstack
inspectheap クラスヒストグラムの取得 jmap
setflag 非推奨オプション(-XX)の設定 jinfo
printflag 非推奨オプション(-XX)の取得 jinfo
jcmd jcmdの実行 jcmd
enabledprobes DTraceプローブを有効にする
※Solarisのみ
なし
11. Javaプログラム ※抜粋
import java.io.*;
import java.nio.file.*;
import com.sun.tools.attach.*;
import sun.tools.attach.*;
:
Path selfProc = FileSystems.getDefault().getPath("/proc/self");
String pidString = Files.readSymbolicLink(selfProc).toString();
HotSpotVirtualMachine selfVM =
HotSpotVirtualMachine)VirtualMachine.attach(pidString);
try(InputStream in = selfVM.heapHisto("-all")){
byte[] buf = new byte[1024];
int n;
while((n = in.read(buf)) > 0){
System.out.print(new String(buf, 0, n, "UTF-8"));
}
}
finally{
selfVM.detach();
}
:
PID取得
アタッチ
AttachListenerへ
コマンド送信
結果読み込み
&
出力
デタッチ
12. ビルド&実行
$ javac -cp $JAVA_HOME/lib/tools.jar AttachListenerTest.java
$ java -cp $JAVA_HOME/lib/tools.jar:. AttachListenerTest
num #instances #bytes class name
----------------------------------------------
1: 13228 1462888 [C
2: 7477 1136592 <constMethodKlass>
3: 7477 1022984 <methodKlass>
4: 562 701568 <constantPoolKlass>
5: 2093 536728 [B
6: 562 396376 <instanceKlassKlass>
7: 502 395520 <constantPoolCacheKlass>
8: 579 265768 [I
9: 6120 146880 java.lang.String
10: 631 76696 java.lang.Class
11: 1003 65664 [S
12: 882 55808 [Ljava.lang.Object;
13: 2278 54672 java.lang.StringBuilder
14: 900 52952 [[I
15: 52 29536 <objArrayKlassKlass>
:
ソケットバッファ
溢れに注意!
14. jvmstat Performance Counters
通称”PerfCounter”
HotSpotの統計情報を集めたファイル
/tmp/hsperfdata_<ユーザー名>/<PID>
各種データは、このファイルにマップされた
仮想メモリ空間に直接保存
GC回数/時間
ライブスレッド数
etc…
各種ツールはこのファイルを直接参照して
JVMの動作状況を収集する
15. 具体的に見てみる
jcmdのPerfCounter.print
$ jcmd 2065 PerfCounter.print
2065:
java.ci.totalTime=0
java.cls.loadedClasses=379
java.cls.sharedLoadedClasses=0
java.cls.sharedUnloadedClasses=0
java.cls.unloadedClasses=0
:
sun.rt.safepointSyncTime=22
sun.rt.safepointTime=80
sun.rt.safepoints=1
sun.rt.threadInterruptSignaled=0
sun.rt.vmInitDoneTime=1365171171705
sun.threads.vmOperationTime=29
sun.urlClassLoader.readClassBytesTime=165372
sun.zip.zipFile.openTime=1010449
sun.zip.zipFiles=2
Javaレイヤから出してるPerfCounter
6878481: Add performance counters in the JDK
16. トラップ!その1
tmpwatch
/tmpや/var/tmpを定期的にお掃除するツール
○ Red Hat系ディストロではおなじみ
/tmp/hsperfdata_<ユーザー名>も消しやがる!
○ psではプロセスが見えるのにJDK付属ツールでアタッ
チできない場合に疑うべきポイント
○ RHBZ#527425 - /tmp/hsperfdata_${user}/${PID} is
deleted by tmpwatch
やっぱり同じことで困ってる人がいました
Fedoraならtmpwatch-2.9.16-1.fc13からOKだそうです
17. トラップ!その2
java.io.tmpdir
6u23、6u24のみ該当
○ 6938627: Make temporary directory use property
java.io.tmpdir when specified
○ 7009828: Fix for 6938627 breaks visualvm
monitoring when -Djava.io.tmpdir is defined
6938627が入ったことで、各種ツールが動かなく
なってしまう人が続出
○ 実はDynamic Attachも影響を受けます
HotSpot混迷の時代
19. Javaプログラム ※抜粋
PerfCounter生成メソッド
sun.misc.PerfCounter#newPerfCounterを
呼び出す
Package privateなのでリフレクションで呼び出し
private PerfCounter newPerfCounter(String name) throws Exception{
Method method = PerfCounter.class.getDeclaredMethod(
"newPerfCounter", String.class);
method.setAccessible(true);
PerfCounter result = (PerfCounter)method.invoke(null, name);
return result;
}
20. Javaプログラム ※抜粋
呼び出し
エントリ名はとりあえず以下のようにしてみる
○ perfCounter.test.<スレッド名>.invocations
String entryName = “perfCounter.test.” +
Thread.currentThread().getName() + ".invocations";
try{
this.perfCounter = this.newPerfCounter(entryName);
22. カスタム版jstat_options
option custom{
column {
header "^Threads^"
data java.threads.live
scale raw
align right
width 4
format "0"
}
column {
header "^MonitorContention^"
data sun.rt._sync_Parks
scale raw
align right
width 4
format "0"
}
column {
header "^STW^"
data (sun.rt.safepointTime/sun.os.hrt.frequency)
scale raw
align right
width 6
format "0.000"
}
column {
header "^ThreadA^"
data perfCounter.test.ThreadA.invocations
scale raw
align right
width 3
format "0"
}
column {
header "^ThreadB^"
data perfCounter.test.ThreadB.invocations
scale raw
align right
width 3
format "0"
}
column {
header "^ThreadC^"
data perfCounter.test.ThreadC.invocations
scale raw
align right
width 3
format "0"
}
}
23. 実行結果
$ jstat -custom 1806
Threads MonitorContention STW ThreadA ThreadB ThreadC
7 21 0.117 7 6 5
インターバル設定しても
変化がみられません
8011934 : sun.misc.PerfCounter calls
Perf.createLong with incorrect parameters
25. Serviceability Agent
通称”SA”
機能
強制的なプロセスアタッチ
○ Linuxではptrace(2)を使用
○ jinfoやjmap等の”-F”オプションで発動
○ jstackでは”-m”オプションでも発動
コア解析
○ LinuxではコアイメージからJavaレイヤの情報(ス
レッドダンプやヒープダンプ)などを取得可能
○ ELFバイナリをdebuginfoまで含め、自力でパース
してしまう!
27. VMStructsの中身
マクロで定義されてます
hotspot/src/share/vm/runtime/vmStructs.cpp
:
nonstatic_field(CollectedHeap, _reserved, MemRegion)
nonstatic_field(SharedHeap, _perm_gen, PermGen*)
nonstatic_field(CollectedHeap, _barrier_set, BarrierSet*)
nonstatic_field(CollectedHeap, _defer_initial_card_mark, bool)
nonstatic_field(CollectedHeap, _is_gc_active, bool)
nonstatic_field(CompactibleSpace, _compaction_top, HeapWord*)
nonstatic_field(CompactibleSpace, _first_dead, HeapWord*)
nonstatic_field(CompactibleSpace, _end_of_live, HeapWord*)
:
28. トラップ!その1
Linux x86_64環境で動かないことがあります
7003789: PTRACE_GETREGS problems with SA
on Linux.
○ JDK6u25、JDK7以降ならFix済み
$ /usr/local/jdk1.6.0_14/bin/jstack -F 8675
Attaching to process ID 8675, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 14.0-b16
Deadlock Detection:
No deadlocks found.
Thread 8693: (state = BLOCKED)
Error occurred during stack walking:
sun.jvm.hotspot.debugger.DebuggerException:
sun.jvm.hotspot.debugger.DebuggerException: get_thread_regs failed for a lwp
29. トラップ!その2
Linux環境でコアイメージを読み込めないこと
があります
7133122: SA throws
sun.jvm.hotspot.debugger.UnmappedAddressExce
ption when it should not
○ 全JDKに影響
○ 2012/01にパッチを出したものの、なかなか入れても
らえない…
$ /usr/local/jdk1.6.0/bin/jstack /usr/local/jdk1.6.0/bin/java core.1249
Attaching to core core.1249 from executable /usr/local/jdk1.6.0/bin/java, please wait...
Error attaching to core file: Can't attach to the core file
31. 1.OpenJDKのソース入手
mercurial (hg) を使います
$ hg clone http://hg.openjdk.java.net/jdk8/jdk8
複製先ディレクトリ: jdk8
全リビジョンを取得中
リビジョンを追加中
マニフェストを追加中
ファイルの変更を追加中
666 個のリビジョン(871 の変更を 114 ファイルに適用)を追加
ブランチ default へ更新中
ファイルの更新数 97、 マージ数 0、 削除数 0、 衝突未解消数 0
$ cd jdk8/
$ sh get_source.sh
# Repositories: corba jaxp jaxws langtools jdk hotspot nashorn
:
リポジトリのclone
forest構成リポジトリの取得
32. 2.パッチ当て
パッチを入手します
CR#7133122のCommentsからコピペ
serviceability-devの投稿メールから取得
○ http://mail.openjdk.java.net/pipermail/serviceability-dev/2012-January/005174.html
JDK8のhotspotでpatchします
$ patch -p1 < address_mapping.patch
patching file agent/src/os/linux/ps_core.c
patch unexpectedly ends in middle of line
Hunk #2 succeeded at 711 with fuzz 1 (offset -1 lines).
35. コアを食べさせてみる
JDK6 GAで試してみました
$ /usr/local/jdk1.6.0/bin/jstack /usr/local/jdk1.6.0/bin/java core.1249
Attaching to core core.1249 from executable /usr/local/jdk1.6.0/bin/java, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 1.6.0-b105
Deadlock Detection:
No deadlocks found.
Thread 1254: (state = BLOCKED)
Thread 1253: (state = BLOCKED)
- java.lang.Object.wait(long) @bci=0 (Interpreted frame)
- java.lang.ref.ReferenceQueue.remove(long) @bci=44, line=116 (Interpreted frame)
- java.lang.ref.ReferenceQueue.remove() @bci=2, line=132 (Interpreted frame)
- java.lang.ref.Finalizer$FinalizerThread.run() @bci=3, line=159 (Interpreted frame)
:
動いた!
38. じゃあ、改造してみる
OpenJDK jcmd改造3分クッキング
とりあえず、コンパイル済みコードの一覧を出し
てみます
○ CodeCacheの内容をダンプしてみます
○ CodeCache.printというコマンドにしてみます
1. OpenJDKのソースを落としてきます
2. jcmdの実装部を追加します
3. ビルドします
これだけ!
41. 2.改造
DiagnosticCommand用クラスの定義
hotspot/src/share/vm/services/diagnosticCommand.hpp
#ifndef PRODUCT
class PrintCodeCacheDCmd : public DCmd {
public:
PrintCodeCacheDCmd(outputStream* output, bool heap)
: DCmd(output,heap) { }
static const char* name() { return "CodeCache.print"; }
static const char* description() {
return "Print compiled methods in CodeCache.";
}
static const char* impact() { return “???"; }
static int num_arguments() { return 0; }
virtual void execute(TRAPS);
};
#endif
どうしよ…?
42. “Impact”とは?
各コマンドのhelpで表示される部分
JEP 137: Diagnostic-Command Framework
定義がものすごく曖昧
○ JVMへの影響度合いをLow / Medium / Highで分類
○ 膨大なスレッドダンプやヒープダンプは深刻、バー
ジョン番号等の取得は大した影響なしと定義
http://openjdk.java.net/jeps/137
$ jcmd 1563 help VM.flags
:
Impact: Low:
:
43. 各コマンドとインパクト
コマンド 内容 インパクト
help コマンドのヘルプ表示 Low
VM.version JVMバージョンの表示 Low
VM.command_line コマンドライン引数の表示 Low
VM.system_properties システムプロパティの表示 Low
VM.flags 非推奨(-XX)オプションの表示 Low
VM.uptime JVM起動時間の表示 Low
GC.run_finalization ファイナライザの実行 Medium
GC.run GCの実行 Medium
GC.heap_dump ヒープダンプの生成 High
GC.class_histogram クラスヒストグラムの表示 High
GC.class_stats クラス統計情報の表示 High
Thread.print スレッドダンプの表示 Medium
ManagementAgent.start JMXエージェントの起動 No impact
ManagementAgent.start_local JMXエージェントの起動(ローカル限定) No impact
ManagementAgent.stop JMXエージェントの停止 No impact
safepoint “Depends on
Java content.”
44. safepoint
いわゆるSTW
アプリケーションスレッド全停止
正式な定義はコチラ
○ http://openjdk.java.net/groups/hotspot/docs/HotSpotGlossary.html#safepoint
jcmdでは、STWを伴うコマンドは最低でも
Medium以上に定義されている模様
Dynamic Attachでは以下がsafepointで動作
jstack
jmap
46. 2.改造
CodeCacheの全走査になるので、とりあえず
Mediumに設定
#ifndef PRODUCT
class PrintCodeCacheDCmd : public DCmd {
public:
PrintCodeCacheDCmd(outputStream* output, bool heap)
: DCmd(output,heap) { }
static const char* name() { return "CodeCache.print"; }
static const char* description() {
return "Print compiled methods in CodeCache.";
}
static const char* impact() { return “Medium"; }
static int num_arguments() { return 0; }
virtual void execute(TRAPS);
};
#endif
47. 2.改造
PrintCodeCacheDCmdの登録
void DCmdRegistrant::register_dcmds()
に初期化コードを追加
hotspot/src/share/vm/services/diagnosticCommand.cpp
#ifndef PRODUCT
DCmdFactory::register_DCmdFactory(
new DCmdFactoryImpl<PrintCodeCacheDCmd>(true, false));
#endif
50. 実行結果
Tomcat7にjcmd
CATALINA_OPTSに-XX:+Verboseを付加
$ ./build/linux-x86_64-normal-server-fastdebug/jdk/bin/jcmd
31376 org.apache.catalina.startup.Bootstrap start
31390 sun.tools.jcmd.JCmd
$ ./build/linux-x86_64-normal-server-fastdebug/jdk/bin/jcmd 31376 CodeCache.print
31376:
java.util.zip.InflaterInputStream.ensureOpen()V alive
java.lang.Integer.rotateLeft(II)I alive
java.util.zip.ZStreamRef.address()J alive
java.lang.String.charAt(I)C alive
java.lang.String.indexOf(II)I alive
sun.misc.Hashing.murmur3_32(I[CII)I alive
java.lang.Object.<init>()V alive
sun.nio.cs.UTF_8$Encoder.encode([CII[B)I alive
java.lang.String.equals(Ljava/lang/Object;)Z alive
java.lang.System.arraycopy(Ljava/lang/Object;ILjava/lang/Object;II)V alive
java.lang.String.lastIndexOf(II)I alive
:
55. HeapStats Community
HeapStats @ IcedTea
http://icedtea.classpath.org/wiki/HeapStats
ML
heapstats@icedtea.classpath.org
http://icedtea.classpath.org/mailman/listinfo/heapstats