B演習(言語処理系演習)第9回 メモリ管理・ごみ集め

Slides:



Advertisements
Similar presentations
アルゴリズムとデータ構造 第2回 線形リスト(復習).
Advertisements

プログラミング演習II 2004年11月 30日(第6回) 理学部数学科・木村巌.
情報基礎演習B 後半第5回 担当 岩村 TA 谷本君.
プログラミング入門2 第10回 動的な領域確保 情報工学科 篠埜 功.
データ構造とアルゴリズム 第10回 mallocとfree
プログラミング言語論 第6回 型 情報工学科 篠埜 功.
情報工学概論 (アルゴリズムとデータ構造)
アルゴリズムとデータ構造 第3回基本的なデータ構造(リスト) 2015/10/28 アルゴリズムとデータ構造 2015.
2007/1/18 山下 諒蔵 佐藤 春旗 前田 俊行 大山 恵弘 佐藤 秀明 住井 英二郎
第8回 プログラミングⅡ 第8回
2006/10/19 山下 諒蔵 佐藤 春旗 前田 俊行 大山 恵弘 佐藤 秀明 住井英二郎
アルゴリズムとデータ構造 2011年6月13日
第4回放送授業.
アルゴリズムとデータ構造 第2回 線形リスト(復習その2).
データ構造と アルゴリズム 第八回 知能情報学部 新田直也.
プログラミング演習Ⅰ 課題2 10進数と2進数 2回目.
データ構造と アルゴリズム 第十一回 理工学部 情報システム工学科 新田直也.
データ構造と アルゴリズム 第四回 知能情報学部 新田直也.
C言語と機械語 田浦.
第3回 配列,構造体,ポインタ ~ データ構造について学ぶための基礎~
イベント通知機構・メモリ保護API.
C言語講座 第3回 ポインタ、配列.
プログラミング言語入門 手続き型言語としてのJava
B演習(言語処理系演習)第8回 評価器 田浦.
マルチスレッド処理 マルチプロセス処理について
Cプログラミング演習 第7回 メモリ内でのデータの配置.
アルゴリズムとデータ構造1 2006年6月16日
精密工学科プログラミング基礎 第10回資料 (12/18実施)
プログラミング 4 記憶の割り付け.
“Separating Regular Languages with First-Order Logic”
アルゴリズムとデータ構造 補足資料11-1 「mallocとfree」
2005年度 データ構造とアルゴリズム 第3回 「C言語の復習:再帰的データ構造」
アルゴリズムとデータ構造 補足資料5-2 「サンプルプログラムsetop.c」
メモリの準備 メモリには、その準備の方法で2種類ある。 静的変数: コンパイル時にすでにメモリのサイズがわかっているもの。 普通の変数宣言
コンパイラ 2012年11月15日
プログラミング入門2 第11回 情報工学科 篠埜 功.
プログラミング入門2 第11回 情報工学科 篠埜 功.
プログラミング言語論 第五回 理工学部 情報システム工学科 新田直也.
アルゴリズムとデータ構造 補足資料5-1 「メモリとポインタ」
オブジェクト指向言語論 第八回 知能情報学部 新田直也.
データ構造と アルゴリズム 第五回 知能情報学部 新田直也.
オブジェクト指向プログラミングと開発環境
メモリとメモリアドレス, ポインタ変数,関数へのポインタ渡し
オブジェクト指向言語論 第六回 知能情報学部 新田直也.
プログラミング言語論 第六回 理工学部 情報システム工学科 新田直也.
精密工学科プログラミング基礎Ⅱ 第5回資料 今回の授業で習得してほしいこと: 構造体 (教科書 91 ページ)
データ構造とアルゴリズム 第11回 リスト構造(1)
アルゴリズムとプログラミング (Algorithms and Programming)
11: 動的メモリ確保 C プログラミング入門 総機1 (月1) Linux にログインし、以下の講義ページ を開いておくこと
Boostのスマートなポインタを使ってみる
アルゴリズムとデータ構造 2012年6月11日
アルゴリズムとデータ構造1 2006年6月23日
11.1 標準ライブラリ関数 11.2 関数呼び出しのオーバーヘッド 11.3 大域変数 11.4 プロトタイプ宣言 11.5 関数引数
アルゴリズムとデータ構造1 2009年6月15日
オブジェクト指向言語論 第五回 知能情報学部 新田直也.
発表者: 稲葉 一浩 複雑ネットワーク・地図グラフ セミナー 2017/1/19
エイリアス関係を考慮した Javaプログラム用静的スライシングツール
プログラムの一時停止時に 将来の実行情報を提供するデバッガ
曖昧なポインタの存在下での Copying Garbage Collection
コンパイラ 第12回 実行時環境 ― 変数と関数 ― 38号館4階N-411 内線5459
アルゴリズムとデータ構造 2010年6月17日
ソフトウェア工学 知能情報学部 新田直也.
11: 動的メモリ確保 C プログラミング入門 基幹2 (月4) Linux にログインし、以下の講義ページ を開いておくこと
プログラミング演習II 2004年11月 16日(第5回) 理学部数学科・木村巌.
プログラミング演習II 2003年12月10日(第7回) 木村巌.
プログラミング入門2 第5回 配列 変数宣言、初期化について
TList リスト構造とは? 複数のデータを扱うために、 データの内容と、次のデータへのポインタを持つ構造体を使う。
プログラミング演習II 2003年10月29日(第2,3回) 木村巌.
情報処理Ⅱ 第8回:2003年12月9日(火).
オブジェクト指向言語論 第六回 知能情報学部 新田直也.
Presentation transcript:

B演習(言語処理系演習)第9回 メモリ管理・ごみ集め 田浦

我々の処理系はmalloc/newを使っている 字句解析器 構文解析結果(構文木) 関数を呼び出すたびに作られる「環境」 式を評価した結果 例: 2.3 + 3.4, [ 1, 2, 3 ], { a : b }, …

復習: C/C++言語で代表的な3種類のメモリ割り当て方法 局所変数・配列  関数呼び出し終了と共に使えなくなる int* f() { int a[1000]; …; return a; } double* g() { double f; …; return &f; } 大域変数・配列  プログラム実行中常に使える.しかし,実行中に「追加割り当て」できない ヒープ : OSから実行時に取得.代表API : malloc (C), new (C++), … 注 : 中でmallocを呼ぶ関数(e.g., strdup)もある おそらく間違い

基本フォーム 関数呼び出し後も使いたいメモリ 局所変数・配列の領域は使うな 何バイト確保すべきか,同時にいくつ確保すべきかわからない 大域変数・配列の領域は使うな そのような時はmalloc/newを使う しかしmalloc/newされた領域をいつfree/deleteすべきかはプログラマの責任…

割り当てたメモリは「いらなくなったら」解放しなくてはならない いつ「いらなくなる」のか あまり気にする必要ないもの 字句解析器 構文解析結果(構文木) 大いに問題となるもの 「環境」 式を評価した結果

実験: mallocしっぱなしだと何が起こるか

GC (Garbage Collection) あるメモリ領域が「いらなくなった」かどうか,自動的に見つけていらなければ解放(再利用)する仕組み Java, Python, Perl, VBA, … (最近の)言語には必ずといっていいほど備わっている C/C++にはそれがないのだが…

天の助け GC ライブラリ http://www.hpl.hp.com/personal/Hans_Boehm/gc/ (Ubuntu) apt-get install libgc-dev C/C++プログラムのmallocをGC_MALLOCに置き換え, freeを除去するだけで再コンパイル&リンクをするだけで,自動的にいらないメモリを解放してくれるライブラリ 演習HP「各種情報」参照

GCの原理

GC以前のメモリ管理の基本原理 メモリ管理モジュールが「現在使われている/いない領域」を把握するデータ構造を構築 割り当て(e.g., malloc(100)) 使われていない領域から,要求サイズを満たす領域を探索 解放(e.g., free(p)) 解放された領域を「使われていない領域」として記録(以降のmallocで利用)

メモリ管理データ構造の例 空き領域リスト(フリーリスト) 使用中アドレス サイズの表(例: ハッシュ表) free 空き領域 使用中領域

例(続) 空き領域リストのデータ表現 typedef struct free_region * free_region_t; typedef struct free_region{ free_region_t next; int sz; } free_region; malloc(sz) { 空き領域リストをたどって要求szを満たす regionを検索; }

GCなしのメモリ管理の危険 メモリ管理ライブラリは盲目的にユーザプログラム(malloc/freeの呼び出し)を信ずる 早すぎるfree 本来別のアドレスに割り当てられるべき複数の領域が同じアドレスに割り当てられる意図しないデータの破壊 メモリリーク freeすべきものをfreeしない 本来他の目的に使われるべきアドレスが再利用されない意図しない使用メモリ領域の増大

自動メモリ管理(ごみ集め; Garbage Collection) 「もう使われない領域」を自動的に発見して解放 プログラムの安全性は格段に向上する GC用語 (プログラム実行中のある時点で) 「生きている領域」 = その時点以降使われる(アクセスされる)領域 反対語: 「死んでいる領域」

もう使われない領域をどう発見するのか? (実現不可能な)理想  プログラムを実行し続けた際に,今後使われる領域とそうでない領域を各時点で100%正しく判定する(一種の未来予知? アルゴリズム自動理解?) main() { …; for (i = 0; i < n; i++) { p = a[i * i]; … *p … } } この地点以降使われるメモリ領域はa[0], a[1], a[4], a[9], a[16], …

実際のGCが行っている「近似」 現在局所・大域変数に入っているアドレス(が指すメモリ領域)は「生きている」 注: アドレスが指す「メモリ領域」  そのアドレスを含む,一回のメモリ割り当てで割り当てられた領域 ある「使われる」メモリ領域中に入っているアドレス(が指すメモリ領域)は「生きている」 配列の要素,構造体のフィールドなど 「今後使われる」  「生きている」

同じことの書き換え LIVE : (ある注目している時点で)「生きている」アドレスの集合 ある局所・大域変数の値 = a  a  LIVE “a  LIVE” かつ “pが,aが指すメモリ領域内に格納されている”  p  LIVE a p

要するに局所・大域変数から「到達可能」なものが「生きている」 局所変数 大域変数

GCの基本原理 割り当て (e.g., malloc(sz);) このとき,空き領域が足りなくなったら(基準は様々), 空き領域からszバイト分の連続した空き領域(a)を発見 [a, a + sz) を使用中と記録 (a をキーとして探索するとszが分かるよう,何らかのデータ構造に記録しておく) このとき,空き領域が足りなくなったら(基準は様々), 1. 生きている領域をすべて見つける(マーク) 2. 残りの領域を解放(スイープ)

マーク 生きているものすべてに「印」をつける 要するにグラフの探索問題(例えばdepth-first) 印の場所: オブジェクトの先頭に専用の領域を作っておく,ハッシュ表などを作る,などがある 要するにグラフの探索問題(例えばdepth-first)

擬似コード: void GC() { mark_stack = mk_empty_mark_stack(); initialize all memory blocks as `not marked’; for each word p in global variable { mark(p); } for each word p in local variable { mark(p); } while (! empty(mark_stack)) { p = pop (mark_stack); for each word q in the memory block pointed to by p { mark(q); } } for all memory block p not marked { free(p); } } void mark(p) { if (! marked(p)) { record p as `marked’; push(p, mark_stack); } }

「mini-Pythonに自力GCを実装」のチャレンジャーへ 最低限必要なこと アドレスa  aが割り当て中領域であるかどうかが判定できる.そうならばそのサイズ 割り当てた領域のアドレスをすべて列挙できる 全局所変数中のポインタの列挙 全大域変数中のポインタの列挙 あるアドレスaで指されている領域中のポインタの列挙

チャレンジャー用tips 要領1: 空き領域管理は,それ自身重労働.これをmallocにまかせると楽かもしれない my_GC_malloc(sz) { a = malloc(sz); aからszバイト割り当て中と記録; return a; } application my_GC_malloc my_GC_malloc malloc free malloc/free

チャレンジャー用tips 要領2 : 主たる手間は「割り当て中領域」を記録するデータ構造 おそらくハッシュ表を作るのが良いだろう キー : アドレス 値 : 割り当てサイズ + 「印」 注: ポインタとしてありえない値(最下位2bitが0でない)はハッシュ表を引くまでもない

全局所変数の列挙 局所変数はスタックに格納されている.ごく一部はレジスタ スタック: main関数における局所変数のアドレスとGCが起動されたときの局所変数のアドレスに挟まれた領域 レジスタ上の値については,挑戦したい人は質問してください(try “man setjmp”) main interpreter (ほぼすべての)局所変数 eval_... GC()

大域変数の列挙 インタプリタ実装時に,大域変数を使わない(大域変数にポインタを置かない)のが一番簡単 使う場合は, 「大域変数のアドレスをGCに登録するAPI」を用意すると良いかもしれない int * global_var; GC_register_root(&global_var, sizeof(int)); 奥の手については,チャレンジする人は質問してください

GCはいつ起動すべきか ×mallocが成功する限り起動しない 自らある制限(使用中メモリサイズ <= A)を課す メモリ使用量が多くなりすぎる 自らある制限(使用中メモリサイズ <= A)を課す Aをいくらにするか? GC終了後に「生きている量」を測り,その最大値×定数(2~3)とするのが基本