https://jjug.doorkeeper.jp/events/23636
感想
- javap と仲良くなろうと思った
- 基本のGCのアルゴリズムが理解できた。すごくスッキリ分かってしまった
- 「パージェクトJava」読み終えたぐらいのレベル感(つまり僕)の人にちょうどいい感じだった
- USTに甘えず来てよかった
- マカロンは積むもの
桜庭さん@skrb
- Javaのプログラムが実際どうやって動くのかというお話
- アジェンダ
- JVMとは
- mainを実行するまで
- どうやって実行するか(バイトコード)
- Javaチャンピオン、日本で一人
- サイト「Java in the Box」
- Java SE
- このプレゼンもJavaFXでつくっています!
- サイト「ごちそうフォト」
- 最近1位になった(称号:スイーツ王)
一部
JVM
- VirtualMachine
- ソフトウェア仮想マシン
- VM(Write Once Run Anywhere)
- OSによらない。OSの差異を吸収
- Java Virtual Machine
- javac → クラスファイル。クラスファイルの中にバイトコード
- バイトコードを解釈して実行
- 基本、インタープリタ(1行ずつ実行) ⇔ Cなどのコンパイル言語
- 実行時コンパイルで速くする(JIT)
- 今日はJITはやらない、インタープリタだけ
- 今日のサンプルコード
- 今日のJavaのコードはこれだけ
- main()と、add()のメソッドがあるだけ
- コンパイル: .java → javac → .class
- .java
- クラス、フィールド、メソッド 定義が入ってる
- .class
- クラス定義、フィールド定義、バイトコード(メソッド)、定数プール(文字列、リテラル)、属性(その他雑多。Genericsの型パラメータなど。Javaのバージョンによって解釈可能・不可能がある。解釈できないと無視する)
- .java
- javap
- バイトコードの逆アセンブル
- オプション
- -p プライベートメソッドも含める
- -v バイトコード、定数プールも含める
- 書籍「Java仮想マシン仕様」 バイトコードの読み方
- Wikipediaにもバイトコードのよみかた載ってる
mainを実行するまで
- java コマンドを叩くと...
- JVM起動→クラスロード→リンク(.classの準備)→初期化→main実行
- JVM起動
- JNI_CreateJavaVM() ←JVMを起動できる
- クラスロード
- Class ファイルの読みこみ、使用するものを芋づるでロード(親クラスとか、利用しているものとか)
- さっきのサンプルコードでも、200個位クラス読み込む
- なにが読み込まれているか確認
- java -verbose:class Test
- rt.jar(超巨大)←java9から分割する?
- リンク
- classファイルが正しいかどうかチェック(改ざん、悪意のあるコード)
- static フィールドの初期化(※コードの実行はしない。オブジェクトはnullが入る、基本型は基本型のデフォルトが入る?)
- クラス間の関係を解決(resolution)
- ここで実行できる準備がととのった!
- 初期化
- Staticフィールドの初期化(今度はコードの実行をする。final変数とかでもココで初めて値が入る)
- static イニシャライザの実行
- main()
- 実行
public class Hoge
static {
//ここ
}
}
- Java Puzzle: Anniversary
- static final でもおかしな値が入る事がある
バイトコードの実行
- コレクションのいろいろ
- 1:Queue
- 2:Deque (両側から出せる)
- 3:Stack (片側からしかだせない)
- 4:Ring Buffer(終わりがない)
- Javaにはない
- 5:Map
- 6:Linked List
- Javaの実装いろいろ
今日のお話のメインは Stack
- Last In Fisrt Out(LIFO)
- 途中要素へのアクセスNG
実行形式
- ☆ StackMachine
- Java VM
- PostScript
- .NET Framework CLR
- ☆ VMは StachMachine が書きやすい
- ☆ RegisterMachine
- メモリを事前に準備
- ☆ IntelCore, ARM Cortex
- ☆ StackMachine
逆ポーランド記法
- カッコがなくてイイ!
- StackMachineと相性がいい
- 中間の状態をStackにとっておく!
ワーキングメモリ
- スレッドごとにスタックがある(Java Stack)
- スタック
- PC(プログラム・カウンタ)
- フレームを積み重ねる
- 再帰で見たことのある、スタックオーバーフロー
- フレームの中に更にスタックがある
- Operand Stack(計算領域?)
- Local Variable(ローカル変数)
- Heapへの参照をもってる
バイトコード(フレーム内の話)
- load : ローカル変数からの読み出し
- store : ローカル変数に入れる
- フレームスタックの0番目にはthisが入る(インスタンスメソッドの実行)
JVM編まとめ&質問
- OracleとEclipseのjavacの出力物はちょっとちがう
- VMの動作はベンダーによってちょっと違う、例えばIBM(一応仕様通りには作られているはず)
- OracleJDKとOpenJDKの関係、コードベースは同じ。Oracleの方がツールが多い、内部的にいろいろやってたり
二部 GC
- なぜGC?
- GC アルゴリズム
- Mark&Sweep
- Copy
- 世代別
- GC
- メモリを自動的に管理
- 不要になったオブジェクトを開放
- GCがなかった頃
- 自前でメモリ管理
- Cとか...
- 問題点
- メモリ開放わすれ
- 二重開放
- 無効な参照
- メモリ周りのバグ修正難しい(静的改正とか、デバッガはあるけれども...)
- 自前でメモリ管理
- 1959年に最初に作られる
- Mark&Sweep(Lisp向け)
1990年代に、やっと使われてきた感じ
- JavaのGCも遅いって言われてた
- CPUやメモリの性能向上
GCの分類
- アルゴリズム
- 基本
- ◯ Mark&Sweep
- 参照カウント(※Javaにはない)
- ◯ Copy
- 複合
- ◯ 世代別
- G1
- 基本
- 運用
- Serial
- Incremental
- Concurrent
- Parallel
- アルゴリズム
↑の◯のSerialを紹介
GCはGC本があるのでそれ読むといいよ
Mark & Sweep
使用中のオブジェクトにマーク。マークがされていないオブジェクトを掃除
- Mark フェーズ
- Sweep フェーズで取り除く
どうやってMarkする? ※要図参照
- Rootから、参照をたどっていく。芋づる式
- ☆深さ優先探索
- Rootから、参照をたどっていく。芋づる式
Mark後、先頭からFreeListに入れる
Mark&Sweapのあと、間に空いた部分を詰める(Compaction コンパクション)
いいところ
- 実装がシンプル、参照書き換えがない(※コンパクションしない場合)、安全
わるいところ
- 遅い
- プログラムを止める必要があるが、その時間が長い
- ヒープの断片化が起きやすい(※コンパクションしない場合)
- ↑コンパクションも重たいので、やったらやったで余計重たい
Copy GC
- ヒープを二分割
- 使ってる方と使ってない方 に二分
- GC時に、まずマークして、使ってる方から使ってない方に、使ってるオブジェクトだけコピーする(コンパクションもできてる)
- ↑をやると使ってる方・使ってない方が入れ替わる
- いいところ
- 速い
- ヒープの断片化がおきない
- 高速なアロケーション(先頭から詰めておくから)
- わるいところ
- ヒープの使用効率が悪い(2倍必要)
- 参照の書き換えがある
世代別GC
- 仮説:若いObjほど早く死ぬ
- 世代別にヒープ管理を行う
- 若い世代:高速なGC → Copy
- 古い世代:安定したGC → Mark&Sweep
- Objの年齢:GCを生き延びた回数
- HotSpotVM
- CopyGC用→Survivor 1 & Survivor 2
- Mark&SweepGC用→Tenured(テニュアード、かな)
- 新しいObjはEdenに。
- GC時、Suvivor1に入れる(CopyGC)
- 次のGC時、EdenとSuvivor1の生き残りを、Survivor2に一緒に入れる
- 何度かGCをやって、まだ残っていたら、Tenuredにいれる
- TenuredのGCでコンパクションが走るのがいわゆるフルGC。めっちゃ遅いので、なるべく避ける
- Mark&Sweepと、CopyGCのいいとこどり
- 領域サイズのチューニングが必要。古い世代のObjのGCをなるべく減らす
- ☆CMS,☆G1GCなどの派生GCもあり
まとめ
- アルゴリズムは3つ!60年代から変わらない。それらの組み合わせで新しいものが出来てきている
- 原理を知っておけば、チューニングに活かせる
- GCは友達!
GCを動かさないようにすることは?
- やり方はある。RealTimeJava(スループット高い)。
- ☆Jロキット、G1GCでGCの上限時間を設定できる
Groovy 、jruby
- 実行時に型を決める
- Java女子部宣伝
- よこたさん
おつかれーした!