Download - 20130819 jjugnslt
Aggressive Heap の実装をAggressive に解析する
JJUG Night Seminar Aug, 2013
Shinya TAKEBAYASHI
自己紹介 v 名前: 竹林 信哉(たけばやし しんや)
v 職業: 某 SIer 社員 ・・・ 少し前まで CGL 作っていました.今は Java サポート.
v 得意: Ø OS 実装 / デバイスドライバ / UNIX / WindowsØ C / C++ / asm(x86, SPARC) / Python / 並列(CUDA)Ø コンパイラ,デバッガとお友達
v 私と JavaØ プログラミング言語 Java は苦手ですØ JavaVM の実装が大好き
@chi9rin
chi9 流 Hack の考え方 v 前提として
Ø アプリケーションは,そう簡単にソースコード通りに動きません.
Ø 環境に依存して,書いたソースをまねて動きます.Ø プログラマやアーキテクトの腕の見せ所は,いかに狙った環境で狙ったとおりに動かすか,です.
chi9 流 Hack の考え方(Cont’d) v だから
Ø ソースコードばかりを追っていても,答えが得られない可能性があります.
Ø ソースコードと実際の動作を見て,初めて実装を理解できます.
ソースコードを読むのは予習 動いているものを trap して解析するのが Hack
-XX:+AggressiveHeap v 大規模アプリケーションにいいよ,という触れ込みの設定
v 自動でヒープサイズを決めてくれる
v 結局何が指定されるかを解説したドキュメントが乏しい
v なのに皆さん勧めている謎の設定
-XX:+AggressiveHeap で何が起きるか
計算 • メモリサイズ • GC スレッド数
機能の有効化 • Parallel GC • Large Pages • JIT コンパイルポリシはシンプルモード
機能の無効化 • Scavenge GC • TLAB の動的リサイズ
julong initHeapSize; julong total_memory = os::physical_memory(); if (total_memory < (julong)256*M) { jio_fprintf(defaultStream::error_stream(), "You need at least 256mb of memory to use -XX:+AggressiveHeap\n"); vm_exit(1); } initHeapSize = MIN2(total_memory / (julong)2, total_memory - (julong)160*M); initHeapSize = os::allocatable_physical_memory(initHeapSize); if (initHeapSize > MaxPermSize) { initHeapSize = initHeapSize - MaxPermSize; } else { warning("AggressiveHeap and MaxPermSize values may conflict"); } if (FLAG_IS_DEFAULT(MaxHeapSize)) { FLAG_SET_CMDLINE(uintx, MaxHeapSize, initHeapSize); FLAG_SET_CMDLINE(uintx, InitialHeapSize, initHeapSize); set_min_heap_size(initHeapSize); } if (FLAG_IS_DEFAULT(NewSize)) { FLAG_SET_CMDLINE(uintx, NewSize, ((julong)MaxHeapSize / (julong)8) * (julong)3); FLAG_SET_CMDLINE(uintx, MaxNewSize, NewSize); } FLAG_SET_DEFAULT(UseLargePages, true); FLAG_SET_CMDLINE(uintx, BaseFootPrintEstimate, MaxHeapSize); FLAG_SET_CMDLINE(bool, ResizeTLAB, false); FLAG_SET_CMDLINE(uintx, TLABSize, 256*K); FLAG_SET_CMDLINE(uintx, YoungPLABSize, 256*K); FLAG_SET_CMDLINE(uintx, OldPLABSize, 8*K); FLAG_SET_CMDLINE(intx, CompilationPolicyChoice, 0); FLAG_SET_CMDLINE(bool, UseParallelGC, true); FLAG_SET_DEFAULT(ParallelGCThreads, Abstract_VM_Version::parallel_worker_threads()); FLAG_SET_CMDLINE(uintx, ThresholdTolerance, 100); FLAG_SET_CMDLINE(bool, ScavengeBeforeFullGC, false); FLAG_SET_CMDLINE(bool, BindGCTaskThreadsToCPUs, true);
答え
パラメータ 最近のマシン環境で設定される値(青字は自動計算されるところ) -Xmx(MaxHeapSize) 物理メモリ量の半分 – 約 85 MB(LP64 の場合) -Xms(InitialHeapSize) MaxHeapSize と同じ-XX:NewSize (MaxHeapSize ÷ 8) × 3 -XX:MaxNewSize NewSize と同値 -XX:+UseLargePages 有効化 -XX:BaseFootPrintEstimates MaxHeapSize と同値 -XX:-ResizeTLAB 無効化 -XX:TALBSize 256KB -XX:YoungPLABSize 256KB -XX:OldPLABSize 8KB -XX:CompilationPolicyChoice 0(SimpleCompilePolicy)-XX:+UseParallelGC 有効化-XX:ParallelGCThreads CPU 数が 8 以下なら CPU 数と同値,9 以上なら後述の計算式-XX:ThreasholdTolerance 100
-XX:-ScavengeBeforeFullGC 無効化-XX:+BindGCTaskThreadsToCPUs 有効化
・ヒープ領域を多め(Aggressive!)に確保する.・AP を全力(Aggressive!!)で走らせる.
・極力 GC しない.GC はまとめて全力(Aggressive!!!)で.
では,Hack を始めます 環境
CPU 4 cores – メモリ 2GB - Debian/GNU Linux 64 bits
JavaVM の準備
% hg clone http://hg.openjdk.java.net/jdk7u/jdk7u2 % cd jdk7u2 % chmod +x ./get_source.sh; ./get_source.sh % export ALT_BOOTDIR=/usr/lib/jvm/java-6-openjdk-amd64 % source jdk/make/jdk_generic_profile.sh % make sanity && make ALLOW_DOWNLOAD=true debug_build
【注意】 ・make -jn しないこと. ・gcc は 4.6 以前を使うこと.clang/clang++ は使わない. ・jdk7u25 以降は ZIP_DEBUGINFO_FILES=0 が必要
JavaVM 起動と進め方 v コマンドラインオプションを解析する箇所にブレイクポイントを仕掛ける.
v signal 6(SIGABRT)を投げて core を分析しても good!
% ulimit –c unlimited % gdb ./java (gdb) b arguments.cpp:2458 No source file named arguments.cpp Make breakpoint pending on future shared library load? (y or [N]) y Breakpoint 1 (arguments.cpp:2458) pending. (gdb) r -XX:+AggressiveHeap ... Breakpoint 1, Arguments::parse_each_vm_init_arg ...... (gdb) signal 6 Continuing with signal SIGABRT. [Thread 0x7ffff7fd0700 (LWP 21391) exited] No unwaited-for children left.
計算 その 1そもそも AggressiveHeap が使えるかどうか
v 物理メモリの算出と閾値判定Ø 物理メモリ量の算出
p = os::Linux::_physical_memory= sysconf(_SC_PHYS_PAGES) * sysconf(_SC_PAGESIZE)= 物理ページ数 × ページサイズ = 513,535 × 4096 = 2,103,439,360= /proc/meminfo の MemTotal 値 × 1,024(KB)
Ø 判定: 256 MB 未満なら AggressiveHeap は使えない
% gdb ./java (gdb) b arguments.cpp:2446 (gdb) r -XX:+AggressiveHeap Breakpoint 1, Arguments::parse_each_vm_init_arg (args=0x7ffff7fcfe20, scp_p=0x7ffff7fcea10, ... (gdb) p total_memory $1 = 2103439360 % grep MemTotal /proc/meminfo | awk '{print $2;}' | xargs -I n expr n \* 1024 2103439360
julong total_memory = os::physical_memory();
if (total_memory < (julong)256*M) { jio_fprintf(defaultStream::error_stream(), "You need at least 256mb of memory to use -XX:+AggressiveHeap\n"); vm_exit(1); }
計算 その 2ヒープサイズの計算
v まず基準になるサイズを求め,次にヒープサイズを確定する.
p total_memory ÷ 2 または total_memory - 160 MB の小さい方= 1,051,719,680 または 1,935,503,360 の小さい方= 1,051,719,680
p = 1,051,719,680 – 87,241,520 = 964,478,160 (← LP64) 1,051,719,680 – 67,108,864 = 984,610,816(← それ以外)
% gdb ./java (gdb) b arguments.cpp:2469 (gdb) r -XX:+AggressiveHeap Breakpoint 1, Arguments::parse_each_vm_init_arg (args=0x7ffff7fcfe20, scp_p=0x7ffff7fcea10, ... (gdb) p MaxPermSize $2 = 87241520 (gdb) p initHeapSize $1 = 964478160
initHeapSize = MIN2(total_memory) / (julong) 2, total_memory – (julong)160 * M);
if (initHeapSize > MaxPermSize) { initHeapSize = initHeapSize - MaxPermSize; } else { warning("AggressiveHeap and MaxPermSize values may conflict"); }
計算 その 2(Cont’d)ヒープサイズの計算
v 確定した initHeapSize を MaxHeapSize,InitialHeapSize として設定.
if (FLAG_IS_DEFAULT(MaxHeapSize)) { FLAG_SET_CMDLINE(uintx, MaxHeapSize, initHeapSize); FLAG_SET_CMDLINE(uintx, InitialHeapSize, initHeapSize); set_min_heap_size(initHeapSize); }
計算 その 3New 領域サイズの計算
v 算出した MaxHeapSize をもとに計算される.
Ø NewSizep (MaxHeapSize / 8) * 3) = (964,478,160 ÷ 8) × 3
= 361,679,310Ø MaxNewSize
p 上記で算出した NewSize と同値
% gdb ./java (gdb) b arguments.cpp:2482 (gdb) r -XX:+AggressiveHeap Breakpoint 1, Arguments::parse_each_vm_init_arg (args=0x7ffff7fcfe20, scp_p=0x7ffff7fcea10, ... (gdb) p MaxHeapSize $1 = 964478160 (gdb) p NewSize $2 = 361679310 (gdb) p MaxNewSize $3 = 361679310
FLAG_SET_CMDLINE(uintx, NewSize, ((julong)MaxHeapSize / (julong)8) * (julong)3); FLAG_SET_CMDLINE(uintx, MaxNewSize, NewSize);
計算 その 4GC スレッド数の算出
v アクティブな CPU (/proc/cpuinfo の数)をもとに確定する.Ø Abstract_VM_Version::nof_parallel_worker_threads()
p nof_parallel_worker_threads(num = 5, den = 8, switch_pt = 8)アクティブな CPU 数に応じて適切な値が計算される.
p アクティブ CPU 数が 8 以下なら,アクティブな CPU 数と同値.p 9 CPU 以上なら,8 + ((アクティブ CPU 数 – 8) × 5) ÷ 8)例)アクティブ CPU 数が 72 なら 8 + ((72 – 8) × 5) ÷ 8) = 48 スレッド.
(gdb) b vm_version.cpp:307 (gdb) r -XX:+AggressiveHeap ... Breakpoint 1, Abstract_VM_Version::parallel_worker_threads () at ... (gdb) p _parallel_worker_threads $1 = 4
unsigned int ncpus = (unsigned int) os::active_processor_count(); return (ncpus <= switch_pt) ? ncpus : (switch_pt + ((ncpus - switch_pt) * num) / den);
おさらい パラメータ 最近のマシン環境で設定される値 GDB での出力 jinfo 出力
-Xmx(MaxHeapSize) 物理メモリ量の半分 – 約 85 MB -XX:MaxHeapSize=964478160
-Xms(InitialHeapSize) MaxHeapSize と同じ -XX:InitialHeapSize=964478160
-XX:NewSize (MaxHeapSize ÷ 8) × 3 -XX:NewSize=361679310
-XX:MaxNewSize NewSize と同値 -XX:MaxNewSize=361679310
-XX:+UseLargePages 有効化 -XX:+UseLargePages
-XX:BaseFootPrintEstimates MaxHeapSize と同値 -XX:BaseFootPrintEstimates=964478160
-XX:-ResizeTLAB 無効化 -XX:-ResizeTLAB
-XX:TLABSize 256KB -XX:TLABSize=262144
-XX:YoungPLABSize 256KB -XX:YoungPLABSize=262144
-XX:OldPLABSize 8KB -XX:OldPLABSize=8192
-XX:CompilationPolicyChoice 0(SimpleCompilePolicy) -XX:CompilationPolicyChoice=0
-XX:+UseParallelGC 有効化 -XX:+UseParallelGC
-XX:ParallelGCThreads CPU 数が 8 以下なら CPU 数と同値 -XX:ParallelGCThreads=4
-XX:ThresholdTolerance 100 -XX:ThresholdTolerance=100
-XX:-ScavengeBeforeFullGC 無効化 -XX:-ScavengeBeforeFullGC
-XX:+BindGCTaskThreadsToCPUs 有効化 -XX:+BindGCTaskThreadsToCPUs
(gdb) p MaxHeapSize $1 = 964478160 (gdb) p InitialHeapSize $2 = 964478160 (gdb) p NewSize $3 = 361679310 (gdb) p MaxNewSize $4 = 361679310 (gdb) p UseLargePages $5 = true (gdb) p BaseFootPrintEstimate $6 = 964478160 (gdb) p ResizeTLAB $7 = false (gdb) p TLABSize $8 = 262144 (gdb) p YoungPLABSize $9 = 262144 (gdb) p OldPLABSize $10 = 8192 (gdb) p CompilationPolicyChoice $11 = 0 (gdb) p UseParallelGC $12 = true (gdb) p ParallelGCThreads $13 = 4 (gdb) p ThresholdTolerance $14 = 100 (gdb) p ScavengeBeforeFullGC $15 = false (gdb) p BindGCTaskThreadsToCPUs $16 = true
最後に v VM の実装を理解する勉強会をやりたい
Ø この手のネタを受け付ける人が身近には少ないので,ここで募集
v JavaOne 2013@SFO 行きます
v 次回予告Ø ByteCode / JIT コンパイラに優しいソースコード