20130819 jjugnslt

17
Aggressive Heap の実装を Aggressive 解析する JJUG Night Seminar Aug, 2013 Shinya TAKEBAYASHI

Transcript of 20130819 jjugnslt

Page 1: 20130819 jjugnslt

Aggressive Heap の実装をAggressive に解析する

JJUG Night Seminar Aug, 2013

Shinya TAKEBAYASHI

Page 2: 20130819 jjugnslt

自己紹介 v  名前: 竹林 信哉(たけばやし しんや)

v  職業: 某 SIer 社員 ・・・ 少し前まで CGL 作っていました.今は Java サポート.

v  得意: Ø  OS 実装 / デバイスドライバ / UNIX / WindowsØ  C / C++ / asm(x86, SPARC) / Python / 並列(CUDA)Ø  コンパイラ,デバッガとお友達

v  私と JavaØ  プログラミング言語 Java は苦手ですØ  JavaVM の実装が大好き

@chi9rin

Page 3: 20130819 jjugnslt

chi9 流 Hack の考え方 v  前提として

Ø  アプリケーションは,そう簡単にソースコード通りに動きません.

Ø  環境に依存して,書いたソースをまねて動きます.Ø  プログラマやアーキテクトの腕の見せ所は,いかに狙った環境で狙ったとおりに動かすか,です.

Page 4: 20130819 jjugnslt

chi9 流 Hack の考え方(Cont’d) v  だから

Ø  ソースコードばかりを追っていても,答えが得られない可能性があります.

Ø  ソースコードと実際の動作を見て,初めて実装を理解できます.

ソースコードを読むのは予習 動いているものを trap して解析するのが Hack

Page 5: 20130819 jjugnslt

-XX:+AggressiveHeap v  大規模アプリケーションにいいよ,という触れ込みの設定

v  自動でヒープサイズを決めてくれる

v  結局何が指定されるかを解説したドキュメントが乏しい

v  なのに皆さん勧めている謎の設定

Page 6: 20130819 jjugnslt

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

Page 7: 20130819 jjugnslt

答え

パラメータ 最近のマシン環境で設定される値(青字は自動計算されるところ) -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!!!)で.

Page 8: 20130819 jjugnslt

では,Hack を始めます 環境

CPU 4 cores – メモリ 2GB - Debian/GNU Linux 64 bits

Page 9: 20130819 jjugnslt

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 が必要

Page 10: 20130819 jjugnslt

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.

Page 11: 20130819 jjugnslt

計算 その 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); }

Page 12: 20130819 jjugnslt

計算 その 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"); }

Page 13: 20130819 jjugnslt

計算 その 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); }

Page 14: 20130819 jjugnslt

計算 その 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);

Page 15: 20130819 jjugnslt

計算 その 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);

Page 16: 20130819 jjugnslt

おさらい パラメータ 最近のマシン環境で設定される値 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

Page 17: 20130819 jjugnslt

最後に v  VM の実装を理解する勉強会をやりたい

Ø  この手のネタを受け付ける人が身近には少ないので,ここで募集

v  JavaOne 2013@SFO 行きます

v  次回予告Ø  ByteCode / JIT コンパイラに優しいソースコード