ファイヤープロジェクト
コンシングとGC
2003-10-23T06:00+09:00   matsu
新しいコンスを作成することをコンシングといい,コンシングすると当然新しいメモリ領域が確保される.こうして新しく作成されたコンスの多くはプログラムのある時点以降は不要なゴミとなる.Lispにおいてもゴミ問題が発生することがある.
Lispでは,メモリ管理に関するコマゴマとした処理(※)をプログラムに記述する必要はない.新しいコンスを作成(コンシング)すると新しいメモリ領域が確保されるが,それはプログラマからすればコンシングという作業として隠蔽される.あるいは,内部でコンシングする関数がコンシングすら隠蔽するかもしれない.こうした仕組みにより,Lispプログラムではプログラマが意識や確認をしなくても,確保されていない領域にアクセスするなどということが発生しない.Lispにダングリングポインタはあり得ない.
※Cでいうとallocやfreeなどの関数,およびプログラマによる領域の保証.
コンシングされたコンスの多くは,プログラムのある時点以降はどのポインタからも参照されなくなる.すなわち参照されない(できない)オブジェクトとなる.
> (setf hoge '(a))    
(A)
> (setf hoge nil)
NIL
> hoge
NIL
この段階で一行目で作成されたコンスへのアクセス手段はなくなる.すなわちそのコンスはゴミとなる.Lisp処理系にはGC(garbage collection)により,参照元のないこれらのゴミを開放してくれる機能がある.この機能のおかげで,Lispにメモリリークはあり得ない.
以上のように,Lispではプログラマにとってとてもありがたい自動的メモリ管理機能がある.ただし,コンシングやGCにはコストがかかる(※).このゴミ問題に対する対処としては「ゴミをつくらない」ことである.すなわち既存のコンスを(再)利用することで,メモリの領域確保と開放の処理を減らすのである.Lispプログラミングの基本方針として,「副作用のない関数呼び出し」というものがある.関数呼び出しでは返り値のみに何かを期待し,関数はそれ以外には影響を及ぼさない(すなわち非破壊的な関数)という考え方である.例えば関数removeは第二引数のリストから第一引数と一致する要素を削除したリストを返す.これは一見第二引数のリストから要素の一部を削除するだけのように見えるが,実際には第二引数で指定した要素と一致する,第一引数のリストの要素以外のコンスをコピーしている.
> (setf lst '(a b c))
(A B C)
> (setf lst2 (remove 'b lst))
(A C)
;; lst2の各コンスはlstのコンスのコピーであることの確認.
> (setf (car lst2) nil)
NIL
> lst2
(NIL C)
> lst
(A B C)
非破壊的な関数の使用はプログラムを分かりやすくすることがあるが,その性質上多くのゴミをつくらざるを得ない.結局のところバランスの問題で,プログラムの重要な部分においては破壊的な関数によってゴミを減らす必要が生じる.Lispでプログラミングする場合は,その関数が破壊的かどうかをある程度意識する必要がある.
※JavaのフルGCの時の負荷を思い出すと恐怖さえ覚える.
matsu(C)
Since 2002
Mail to matsu