2019/11/23 JJUG CCC 2019 Fall - 日本Javaユーザーグループ 登壇資料です。
JVM⼊⾨ Javaプログラムが動く仕組みAbe Asami (きの⼦)
View Slide
⾃⼰紹介"CF"TBNJl͖ͷࢠz େࡕͷϑϦʔϥϯεϓϩάϥϚIUUQOPDPOPOFU!BBUI+BWB,PUMJO4DBMBͳͲͳͲ
最近やっていることhttps://www.geekhub.jp/ ΤϯδχΞಛԽ߹ಉઆ໌ձΠϕϯτɻ࣍ճ 1/25() ։࠵༧ఆʮJava11ʹΑΔ WebΞϓϦέʔγϣϯ։ൃͰֶͿ Javaϓϩάϥϛϯάೖʯൢചதhttps://www.udemy.com/java11-springbootweb-java/
このセッションのターゲット• JavaͰ։ൃΛ͍ͯ͠Δ͕ɺJVM͕ԿΛ͍ͯ͠Δͷ͔Α͘Βͳ͍ਓ• ώʔϓɺGCɺOutOfMemoryɺJITɺHotSpotɺGraal…ͳͲͳͲ͍ΖΜͳ༻ޠΛࣖʹ͢Δ͕Α͘Θ͔Βͳ͍
このセッションのゴール• JVMपΓͷ༻ޠ͕;ΜΘΓཧղͰ͖ΔΑ͏ʹͳΔ• JVM͕ԿΛ͍ͬͯΔ͔͕ͳΜͱͳ͘Θ͔ΔΑ͏ʹͳΔ• JVMपΓͰࠔΓ͝ͱʹૺ۰ͨ͠߹ʹͲ͏ղੳͯ͠ରॲ͢Ε͍͍ͷ͔ɺΞλϦΛ͚ͭΔ͜ͱ͕Ͱ͖ΔΑ͏ʹͳΔ
JVMとは?
JavaVM (Java仮想マシン)• JavaόΠτίʔυΛ࣮ߦ͢ΔͨΊͷԾڥ• OSґଘΛٵऩ
JDK‧JREとJVMの関係• JRE(Java Runtime Environment)• JavaΛ࣮ߦ͢ΔͨΊʹඞཁͳιϑτΣΞηοτ• JVMΛؚΉ• JDK(Java Development Kit)• JavaͷιϑτΣΞ։ൃΛߦ͏ͨΊʹඞཁͳιϑτΣΞηοτ• JREΛؚΉ
JavaのコードがJVM上で動くまで
Javaソースコード
コンパイル
Javaバイトコードを含むクラスファイルが⽣成されるバイナリファイルであるためそのままだと読めないので、「javap」コマンドで⼈間に読める形に逆アセンブル
実⾏
Hello! World!
実⾏の流れᶃ JVMىಈᶄ ϩʔυ (Ϋϥεϩʔμʔ͕ඞཁͳΫϥεϑΝΠϧΛಡΈࠐΉ)ᶅ ϦϯΫ(Ϋϥε͕ഁଛ͍ͯ͠ͳ͍͔ͷݕূࢀরͷղܾ)ᶆ ॳظԽ(staticϑΟʔϧυͷॳظԽͳͲ)ᶇ mainϝιου࣮ߦ
バイトコードは1⾏ずつ実⾏される
でもそれだと処理速度が遅い‧‧‧
「JITコンパイラ」ͦ͜Ͱొ͢Δͷ͕
JITコンパイラ (Just In Time Compiler)• ࣮ߦ࣌ʹίϯύΠϧΛߦ͍ॲཧΛ࠷దԽ͢Δ• ྫ͑Α͘ݺͼग़͞ΕΔॲཧʹ͍ͭͯ࠷దԽΛߦͳ͏ͳͲɺ࣮ߦ݁ՌΛݩʹॲཧΛߦͳ͏• JavajavacʹΑΔΫϥεϑΝΠϧͷίϯύΠϧͱɺ͜ͷ࣮ߦ࣌ͷίϯύΠϧ͕͋Δ
Javaバイトコードは「命令」• JavaԾϚγϯͷ໋ྩηοτ• ྫʣ i2fɾɾɾintΛfloatʹม faddɾɾɾfloatͷՃࢉΛߦͳ͏
JVMはスタックマシン• ʮελοΫʯͱݺΕΔྖҬʹ໋ྩΛPushɾPop͠ͳ͕ΒɺϓϩάϥϜΛ࣮ߦ͍ͯ͘͠
実⾏イメージバイトコードの命令を1⾏ずつ実⾏変数の値を読み込んでPushするという命令
値を2つpopし、⾜した結果をPushするという命令
このスタックは 「オペランドスタック」
メソッドを呼び出すと 「フレーム」が作成されます
「メソッドフレーム」これもスタック
スタックトレース• ελοΫͷग़ྗ• ϝιουͷݺͼग़͠ͷཤྺ
スタックオーバーフロー• ελοΫͷڐ༰ྔΛ͑ΔͱʮStackOverflowErrorʯ
スレッドスレッドの中に1つのスタック(メソッドフレーム)がある
Javaはマルチスレッド
スレッドダンプ• ͱ͋ΔॠؒͷεϨου(ελοΫͳͲ)ͷঢ়ଶΛग़ྗͨ͠ͷ• jstack ɾjcmdίϚϯυͰग़ྗ͢Δ͜ͱ͕Ͱ͖Δ• τϥϒϧൃੜ࣌ͷௐࠪύϑΥʔϚϯεɾνϡʔχϯάʹཱͭ
JVMの種類について
JVMには種類がある• JVMͷ༷JavaԾϚγϯ༷ʹΑͬͯنఆ͞Ε͍ͯΔ͕ɺநత• classϑΝΠϧΛಡΈࠐΜͰਖ਼࣮͘͠ߦͰ͖ΕOKͰ͋ͱJVMͷ࣮ऀ࣍ୈ• ֤छϕϯμ͕ಠࣗJVMΛఏڙ͍ͯ͠Δ
HotSpotVM• Oracleఏڙ• Ұ൪ϝδϟʔͰɺେମͷਓ͜ΕΛ͍ͬͯΔഺ• HotSpotͱ͍͏JITίϯύΠϥΛ࠾༻
GraalVM• ͜ΕOracleఏڙ• Graalͱ͍͏JITίϯύΠϥΛ࠾༻͍ͯ͠Δ• RubyPythonͳͲଞݴޠΛ࣮ߦ͢Δػೳ• AOTίϯύΠϧͰͷωΠςΟϒΠϝʔδԽ͞Ε͍ͯΔ࠷ۙΑ͘ฉ͘
AOTコンパイル• ࣄલίϯύΠϧ• JITͱҟͳΓɺJVMىಈલʹωΠςΟϒίʔυ(=OS͕ղऍͰ͖Δίʔυ)ʹίϯύΠϧ• Java9͔Βಋೖ• HotSpotVMͰ࣮ݧతʹ(?)ಋೖ͞Ε͍ͯΔちょっとややこしいけど最近よく名前が出る‧‧‧
JVMのメモリ管理
前提条件• HotSpot VM Java8• όʔδϣϯJVMʹΑͬͯࠩҟ͕͋ͬͯ ͍͜͠ͷͰ
JVMのメモリ構造
プログラムで⽣成したオブジェクトはHeapに割り当てられる
これが「GC」
GC(GarbageCollection)• ·ͣɺෆཁͱͳͬͨΦϒδΣΫτΛ୳͢ɻ• ෆཁͱஅͨ͠ϝϞϦΛղ์͢Δɻ
ヒープのサイズはオプションで 設定することができる% java -Xms600m -Xmx800m -Xloggc:gc.log -XX:+PrintGCDetails -XX:+PrintGCDateStamps Main-Xms ɾɾɾώʔϓͷॳظαΠζ -Xmx ɾɾɾώʔϓͷ࠷େαΠζ
GCはとてもコストがかかります• GC༻ͷεϨου͕ඞཁ• GCରͷΞϓϦέʔγϣϯεϨουΛࢭΊͯ͠·͏GCʹΑΓ࣌ؒΞϓϦέʔγϣϯ͕ࢭ·ΔݱΛ Stop the World (STW) ͱݺͿ
GCには⾊々なアルゴリズムがある• Mark&Sweep• Copy• ੈผ
GCには⾊々なアルゴリズムがある• Mark&Sweep• Copy• ੈผこれらのアルゴリズムを基本に、各JVMが独⾃でGCを実装している
HotSpotVMのGCの種類• γϦΞϧܕ• ύϥϨϧܕ• CMS• G1※ Java にはShenandoah という新しいGCが実験的に追加されているそうです。基本はJVM任せでOKだが、 シビアなチューニングが必要な場合は それぞれのメリット‧デメリットを踏まえて選択する。 (スループット重視かレスポンス重視か‧‧‧)
いろいろあるけど 「世代別」が基本となっているので世代別GCについて説明します
ヒープには「Young領域」と「Old領域」があります
オブジェクトはまずYoung領域に作られます
Young領域がいっぱいになってきたら‧‧‧
Young領域だけGCが実⾏されます
これを「マイナーGC」といいます
GCされず、⽣き残った 回数はカウントされます
マイナーGCを繰り返す中でいつまでも 残り「歳をとった」オブジェクトは‧‧‧
Old領域に移動します
もしOld領域もいっぱいになってきた場合‧‧‧
ヒープ全域にGCが⾛ります
これを「メジャー(Full )GC」といいます
実際のプログラムでGCの状況を解析してみる
GCログ• GCͷॲཧ࣌ؒલޙͷϝϞϦ༻ྔͳͲͷϩά
GCViewer• GCϩάͷϏϡʔΞʔ• https://github.com/chewiebug/GCViewer• 2019/07/18 ݱࡏɺJDK11ະରԠ
とりあえず、なんかしら動かしてGCログを出してみる
参照が残らないインスタンスを ひたすら⽣成
実⾏java -Xloggc:gc.log -XX:+PrintGCDetails -XX:+PrintGCDateStamps MainGCϩάग़ྗͷͨΊͷΦϓγϣϯ(※ඪ४ग़ྗʹग़͚ͩ͢Ͱ͋Ε -verbose:gc ͚͚ͩͭΕOK)
出⼒されたログ
GCViewerで開いてみる
ΦϒδΣΫτ·ͣYoungྖҬʹ࡞ΒΕɺ ੜ͔Β͕࣌ؒܦͭͱOldྖҬʹҠಈ͢Δ
ࠓճOldྖҬͷҠಈ͕ൃੜ͠ͳ͍ͨΊ ώʔϓશମͷ༻ྔ ≒ YoungྖҬͷώʔϓ༻ྔ
GC࣌ʹͪΌΜͱϝϞϦ͕ղ์͞Ε͍ͯΔ
ϚΠφʔGCɾɾɾYoungྖҬͷΈ͕ରYoungྖҬ͕ ͍ͬͺ͍ʹͳͬͨ λΠϛϯάͰ ϚΠφʔGC͕ ࣮ߦ͞Ε͍ͯΔ
もう1パターン 試してみる
参照が残るインスタンスを ひたすら⽣成
GCログ
これもGCViewerで開いてみる
Full GC (ϝδϟʔGC)ɾɾɾશώʔϓྖҬ͕ର
Full GCは時間がかかる2019-07-18T07:28:35.759-0900: 53.145: [GC (Allocation Failure) [PSYoungGen: 1153507K->190965K(1768960K)]1400776K->506242K(2118656K), 0.3026525 secs] [Times: user=2.55 sys=0.14, real=0.31 secs]2019-07-18T07:28:36.064-0900: 53.450: [Full GC (Ergonomics) [PSYoungGen: 190965K->190932K(1768960K)] [ParOldGe315276K->290297K(610816K)] 506242K->481229K(2379776K), [Metaspace: 2676K->2676K(1056768K)], 4.7517463 secs][Times: user=29.92 sys=0.06, real=4.75 secs]্ͷϚΠφʔGC ɾɾɾ 0.31 secs ԼͷFull GC ɾɾɾ 4.75 secs
ヒープダンプ• ಛఆͷ࣌ࠁʹώʔϓʹؚ·ΕΔͯ͢ͷΦϒδΣΫτͷεφοϓγϣοτjava -Xms600m -Xmx800m -Xloggc:gc.log -XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:+HeapDumpOnOutOfMemoryError Main-XX:+HeapDumpOnOutOfMemoryError ɾɾɾ OOMEൃੜ࣌ʹώʔϓμϯϓΛग़ྗ
まとめ• ීஈͷ։ൃͰ͋·Γҙࣝ͢Δ͜ͱͳ͍͚ΕͲɺಛʹӡ༻ϑΣʔζͰԿ͔τϥϒϧ͕͋ͬͨ߹ʹɺJVMͷ͜ͷลͷ͕ࣝ͋Δͱ߄ͯͳͯ͘͢Ή (ଟ)
ご清聴ありがとうございました。
参考資料• JavaԾϚγϯ༷ (The Java series)• ༰͕ݹ͍ͷͰҙ• JavaύϑΥʔϚϯενϡʔχϯά ୈ2൛• JavaͷϓϩάϥϜͲ͏ͬͯಈ͍͍ͯΔͷ? JVMฤ https://www.slideshare.net/skrb/java-jvm• JavaͷϓϩάϥϜͲ͏ͬͯಈ͍͍ͯΔͷ? GCฤ https://www.slideshare.net/skrb/java-gc-47402594
• OutOfMemoryError ͷௐํ - Qiita https://qiita.com/opengl-8080/items/64152ee9965441f7667b• JavaͲͷΑ͏ʹಈ͘ͷ͔ʙਤղͰΘ͔ΔJVMͷΈɿ࿈ࡌʛgihyo.jp … ٕज़ධࣾ http://gihyo.jp/dev/serial/01/jvm-arc• ʮϝϞϦʔΛҙࣝͯ͠ΈΑ͏ʯୈ2ճɹGCͷΈΛཧղ͢Δ | ܦxTECHʢΫϩεςοΫʣ https://tech.nikkeibp.co.jp/it/article/COLUMN/20060612/240657/?rt=nocnt• ·͡ΊʹJVMνϡʔχϯά: ୈ1ճ ·ͣݱঢ়֬ೝ https://x1.inkenkun.com/archives/367