Transcript PPT - 情報学群
コンパイラ 2012年11月15日 酒居敬一@A468([email protected]) http://www.info.kochi-tech.ac.jp/k1sakai/Lecture/COMP/2012/index.html 1 コンパイラと実行環境の連携2 メモリに関係するバグを未然に防ぎたい 使用されていないメモリの自動回収・再利用 ごみ集め(Garbage Collection) メモリの断片が、使用中かどうかを判定 今回の内容 GCのアルゴリズム トレース式 2 印掃式GC 複写式GC 参照カウント式 トレース式GCの考え方 「参照する」「参照される」という関係を有向グラフ化する Javaでは、オブジェクト変数は「参照する」関係の出発点 Javaでは、基本型は値型なのでここでは無関係。 木ではないが出発点なので、図では「ルート」としている 「参照される」対象はインスタンス インスタンスにもオブジェクト変数を定義できるので、グラフになる GCのアルゴリズム 印掃式 複写式 3 すべてのルートからグラフをたどり、インスタンスに印をつける 最後まで印のつかなかったインスタンスを回収・再利用する ヒープを2つにわけ、一方の領域がいっぱいになるまで割り付ける 空きがなくなったとき、他方の領域へルートからたどりつつコピーする レジスタ群 レジスタ R1 データ構造A … レジスタ R32 大域変数群 大域変数 a 大域変数 b データ構造B データ構造C 実行時スタック SP→ 空き領域 スタックフ レーム C スタックフ レーム B スタックフ レーム A ルート 4 データ構造D データ構造E ヒープ領域 印掃式GC (138ページ a) 順に割り付けて使用中 印 レジスタ群 レジスタ R1 データ構造A … レジスタ R32 大域変数群 大域変数 a 大域変数 b 印 データ構造B 印 データ構造C 実行時スタック SP→ 空き領域 スタックフ レーム C スタックフ レーム B スタックフ レーム A ルート 5 印 データ構造D データ構造E ごみ、空き領域に還元する ヒープ領域 印掃式GC (138ページ b) GCが作業中 to 領域 レジスタ群 from 領域 データ構造A レジスタ R1 … レジスタ R32 データ構造B 大域変数群 大域変数 a 大域変数 b データ構造C 実行時スタック SP→ 空き領域 スタックフ レーム C スタックフ レーム B スタックフ レーム A ルート 6 データ構造D データ構造E ヒープ領域 複写式GC (140ページ a) 順に割り付け中 レジスタ群 レジスタ R1 from 領域 to 領域 データ構造A データ構造A 済 … レジスタ R32 大域変数群 大域変数 a 大域変数 b 済 データ構造B データ構造B 済 データ構造C データ構造C 実行時スタック SP→ 空き領域 スタックフ レーム C スタックフ レーム B スタックフ レーム A ルート 7 済 データ構造D データ構造D データ構造E ヒープ領域 複写式GC (140ページ b) 空きがなくなりGC起動 複写式GCにおけるデータ構造の移動 複写の際にインスタンスが移動する ルートからたどっている最中に参照を更新する 参照は、Javaではオブジェクト変数、Cではポインタ 参照への参照(ポインタへのポインタ)を引数に関数を呼ぶ。 1. void updateReference(reference *field){ /* fieldはポインタへのポインタ */ 2. reference ref = *field; 3. if(ref != null){ 4. if(!alreadyMoved(ref)){ 5. reference new_address = copyToToArea(ref); 6. *field = new_address; 7. markAsMoved(ref, new_address); 8. } 9. else { 10. *field = getForwardPointer(ref); 11. } 12. } 13. } 8 参照カウント式GC データ構造の中にカウンタを設けておく 参照されると+1、参照されなくなったら-1する そのインスタンスを参照しているオブジェクト変数の数になる オブジェクト変数への代入時に処理 カウンタの値が0になれば空き領域に加え再利用する 欠点もある カウンタを操作するオーバーヘッドが大きい 「参照する」「参照される」関係グラフにサイクルがあるとき、 そのサイクル全体がルートから到達不可能でも使用中と 誤認識される 9 メモリリークという致命的欠陥 2 レジスタ群 レジスタ R1 データ構造A … レジスタ R32 1 データ構造B 大域変数群 大域変数 a 大域変数 b 2 データ構造C 実行時スタック SP→ 空き領域 スタックフ レーム C スタックフ レーム B スタックフ レーム A ルート 10 1 データ構造D 1 データ構造E ヒープ領域 参照カウント式GC (141ページ) GCとコンパイラの連携 そもそもポインタが何時何処に存在するのか? 代入以外にも、ポインタからの参照がなくなるときがある 自動変数として消滅するとき インスタンスが消滅するとき ポインタとそれ以外の区別方法 参照の在処をコンパイラが提供する 11 データ構造中の参照 実行時スタックおよびレジスタ データ構造中の参照の在処 class Data { int field1; Data field2; Data field3; クラスを表すデータ構造 Data … … クラスDataのインスタンス クラス名 フィールド数 3 フィールド情報 ヘッダ クラス field1 field2 field3 12 フィールドに関する情報 フィールド名 オフセット 型 field1 2 int field2 3 Data field3 4 Data データ構造の走査 1. void scanDataStructure(reference ref){ 2. class *k = reference->ヘッダ.クラス; 3. int number_of_fields = k->フィールド数; 4. FieldInfo *fields = k->フィールド情報; 5. 6. for(int i = 0; i < number_of_fields; i++){ 7. FieldInfo *field = &fields[i]; 8. 9. if(is_reference(field->型)){ 10. updateReference(&(ref[field->オフセット])); 11. } 12. } 13. } 13 実行時スタックおよびレジスタ中の参照 戻り番地 3 4 12 21 22 23 24 27 33 34 14 変数数 3 3 4 3 3 3 3 3 2 3 変数情報 名前 型 格納先 オフセット あるいは レジスタ名 ee o ExecEnv* void レジスタ スタックフレーム R3 -20 実行時スタックおよびレジスタの走査 1. void scanStackFrames(){ 2. StackFrame *frame = lastFrame(); 3. 4. while(frame > stack_bottom){ 5. PCInfo *info = InfoForPC*frame->return_address); 6. int number_of_variables = info->変数数; 7. VariableInfo *variables = info->変数情報; 8. 9. frame = frame->previous_frame; 10. for(int i = 0; i < number_of_variables; i++){ 11. VariableInfo *variable = &variables[i]; 12. 13. if(is_reference(variable->型)){ 14. if(is_register(variable->格納先)){ 15. updateReference(&frame[variable->オフセット]); 16. } 17. else { 18. updateReference(addressFor(frame, variable->レジスタ名)); 19. } 20. } 21. } 22. } 23. } 15 Q末試験 日時 2012年11月22日2時限目 場所 A106(いつもの講義室) 時間 10時40分から12時10分までの1時間30分 開始後30分過ぎたら退室可 開始後30分までは入室可(遅刻限度) 持ち込んでいいもの 紙の資料、紙の書籍 (紙媒体であればいい) 16