flat7th

+ アペタイト フォー デストラクション

created 2011-02-12 modified 2014-08-13 

Appetite For Destruction
2011/2/13 KURUMA Keizo
2011/3/21 誤字修正、定量記述追加
2011/6/16 リンク追加

ガベージコレクションはブログラマを楽にしたのか?


ガベージコレクションは「今日的な」プログラミング言語製作者には大人気だ。
RubyもD言語もGC言語になっている。

学術的な面での成果はあったのだろう。循環参照に対応するために、世代型ガベージコレクションが開発されたり、強い参照、弱い参照などの研究は進んだと思う。メインプログラムの応答性を改善しようとするコンカレント型も開発されてはいる。

だが、考えてみてほしい。ガベージコレクションは、コンピュータシステム開発ヒエラルキーの末端にいる私達、現場のエンジニアを、本当に楽にしてくれたのか?
そろそろ、「ガベージコレクションの次」を考えてもいいのではないか。

GC言語の危うさ


GC言語は、危うい。

Java にもメモリリークは存在する。
Java にはポインタがないのではなく、Java ではオブジェクトをポインタ以外で扱う事が出来ない。
Java には強い参照と弱い参照があり、つまりは、あるクラスを強く参照しているのか弱く参照しているのかを意識して使い分けなければならない。
ガベージコレクションがいつ走るかはプログラムから制御できないので、不定期に動作が極端に遅くなる。

C#はどうか。C#でのデストラクションは、奇妙な混乱状態になっている。デストラクタ、ファイナライザ、ディスポーズ、クローズの各メソッドが乱立しており、それらの意味と使い分けかたを整然と把握するには、.NETの元となったC++、VB6のデストラクションまで把握する必要がある。
ガベージコレクション機構が解放してくれるのはヒープ上に確保したメモリだけだ。ファイルやソケット、データベースコネクション等、ハンドル型のリソースの解放はしてくれない。だからプログラマは、明示的に解放するオブジェクトと、解放しないオブジェクトを、意識して使い分けなければならない。

使い分けを意識しなければならないなら、プログラマは楽にはなっていない。全部統一しているほうがわかりやすい。
また、VMの実装をいじることができない限り、GCのチューニングは、プログラミングとは別の高度な技術となる。プログラミングで全部制御できるほうが、技術ドメインが狭くなって楽なのではないか。

コンピュータに何をさせるべきか

坂村 健 先生がおっしゃったことを、記憶を頼りに引用する。



コンピュータが得意なことはなにか。それは、与えられた指示どおり正確に、飽きもせず繰り返し、高速に処理を実行すること。コンピュータに人間の代わりに考えさせようとするのではなく、コンピュータにはコンピュータの得意なことをさせ、人間は人間がやるべきことをしなければならない。



コンピュータが得意なことは、つまり、超正確に、超単純なことを、超高速に行う、スーパーパシリ君だ。

ガベージコレクションの信奉者であっても、ガベージコレクション型の言語は、リアルタイムシステムには向かない、という。

コンピュータに高速処理を任せられなくてどうする?
GC言語は軒並みエキスパートシステムや人工知能を作ろうとしているのか?

職業エンジニアがプロとして作るべきもの

京都産業大学時代、謝 章文 先生は「ソフトウェア工学」(ソフトウェア エンジニアリング)の講義で、だいたい以下のようなことをおっしゃった。



用語「プログラム」と「ソフトウェア」をどう使い分けるか。厳密な区分けはないが、この講義では、「ソフトウェア」を、「企業の資産となるべき品質で作ったプログラム」「長期にわたりメンテナンスしていくプログラム」と考える。

プログラムなんて中学生でもつくれる、と誰かが言うかもしれない。使い捨てのプログラムなら確かにそうだろうが、良きにつけ悪しきにつけ、企業が開発したプログラムは、会社の資産となり、長期に渡りメンテナンスされていく。当初開発したメンバーが抜け、別のメンバーがソースを改造する。それはプログラムというよりソフトウェアであり、高度な知識を学んだ者が、
・可読性(読みやすい)
・可変更性(修正しやすい)
・可管理性(手に負える状態を保ちやすい)
の高い品質で作らなければならない。
(注記:複数の意味で、天才プログラマは別クチ)

ハードウェア分野で「エンジニアリング」とは、わかりやすく言えば設計ぴったりの耐用寿命で工業製品がバラバラに壊れること。
例えば川にかける橋を設計するときは、あらかじめ材料を壊して、どのくらいの力をかければ壊れるかを調べた上で、三角やアーチに組み、理論的に耐えられる最大の力を設定する。それ以上の力がかかれば、壊れるように作る(しかない)。
例えばポータブル音楽プレイヤーは、頑丈につくれば重くなる。薄くつくれば壊れやすくなる。第一世代の製品を出した後、故障で修理依頼が多く来た部品は次の世代で頑丈に、修理依頼が来なかった部品は軽くする。そうすると、設定した寿命が来た瞬間にあっちが壊れ、こっちが壊れ、となり、ユーザは買い換えどきだな、と思う。(つまりその意味で俗にいう「ソニータイマ」は高度に設計された結果だということ)

ソフトウェアに、ハードと同じ考えで「エンジニアリング」をしようとして、うまくいかなかった歴史がある。プログラムは、工業製品とちがって経年劣化しない。バグが「顕在化」するだけ。だから、ソフトウェアの耐用寿命を高めるには、最初から間違いが入り込まないように作るしかない。



(この後、構造化設計手法に対して、より新しいオブジェクト指向設計手法の話につづくのだが、当時はまだ「フレームワーク」の概念が一般的でなく、いわゆる「ナイーブ」なオブジェクト指向、私の言葉では「古いオブジェクト指向」だったので、割愛する。古い/新しいオブジェクト指向については"+ オブジェクト指向について徒然"に少し記載。)

言いたいのは、プロなら管理可能なものを作らなければならないということ。そして「エンジニアリング」とは、破壊を制御することだとも解釈できるということ。

破壊を制御して動作速度の安定を手に入れる

私は、「プロがつくるプログラム」としてのソフトウェアは、オブジェクトを作ることより破壊することを制御できるべきであると考える。

C++では、スタック上のオブジェクトとして(auto変数として)作成したオブジェクトは、スコープの終わりで、コンパイラが責任を持ってデストラクタを呼んでくれる。
下位の深い関数で例外が発生し、はるか上位の例外ハンドラまで飛ぶような場合であっても、必ずコンパイラの責任でデストラクタを呼んでくれる。
これは、オブジェクト指向でプログラミングが可能であるとうたう、現時点で業界でメジャーな言語のうち、ほぼC++だけが持つ特徴ではないかと認識している。
C++なら、破壊を完全に制御できる。

私はこの性質を利用して、ある制御システムを、一切new/deleteを用いないで作成したことがある。この仕事が成功した要因は他にもいろいろあるだろうが、少なくとも、ガベージコレクションに頼らず、しかも簡単にオブジェクトの削除を制御できたことは、私の心をとても平らにしてくれた。

ガベージコレクションが必要なほどヒープメモリをたくさん使いたいという意見に反対する根拠として、単純化して例示しよう。

まずは定常的に生存するクラスについて。
main()が、クラスMainを自動変数として生成する。プロセスの生存期間とメインクラスの生存期間は一致するのだから、生存期間が外部からのトランザクション処理単位でないものは、全部メインクラス配下の所有関係になっていればよく、new/deleteする必要はない。main()のスコープ終了時、コンパイラが生成したコードが全クラスを破壊してくれる。(図1)

図1:プロセス全体に関わるオブジェクトの所有例

つぎに、外部に提供するサービスの処理について。
自プロセス宛のメッセージ、自プロセス発のメッセージは、それぞれ自動変数で作成して、処理できる。new/deleteする必要は何もない。

図2:トランザクション処理に必要なオブジェクトの生成例

...これじゃ例が足りないな。こんな場合はどうするんだ?という話もいろいろあるだろうが、要は、ヒープを使わないフレームワークだって作れるということ。

(追記:この案件は1年のプロジェクトで言語はC++、私自身が有効行を18.5kstep、私の元でもう一人が17.5kstepを書き上げ、バグ率は標準よりやや低かった。ある他社のメンバーは5kstep、あるプロパー社員が2kstepといった状況)

まとめ

私はプロのプログラマ、つまりソフトウェアエンジニアとして、オブジェクトの生成よりも、オブジェクトの破壊を制御したい。ガンズ アンド ローゼズの最初のアルバムタイトルのように、デストラクションへの欲求に、飢えている。

ヒープメモリにオブジェクトを生成し、ガベージコレクションで破壊する手法に反対を唱える。
スタックメモリにオブジェクトを生成し、スコープとともにコンパイラに破壊させる手法に賛成する。

近い将来、GC型でない近代的なプログラミング言語の設計と、GCに頼らずオブジェクトを簡単に破壊する手法の普及に携われないかと考えている。


参考リンク

参考リンク備考
Why I hate Java
Javaスクールの危険
+ GLibメモ