OO言語とメモリ割当

「ヒープ領域」「スタック領域」といっても結局、物理メモリ上の領域に対して
VM等で役割毎に適切なデータ構造を割当て、物理メモリ上の領域を分割し、
分割したメモリ領域をデータ構造毎に呼んでいるだけ、結局は同じ物理メモリ上。


我々が日々目にする(あるいは構築する)アプリケーションで「ヒープ領域」
「スタック領域」それぞれに割り当てた場合のコストを論じるのは多くの
場合ナンセンスです。
これらのアプリで、インスタンス変数にするかローカル変数にするかで
発生するメモリ割当のコストの違いはたかがしれています、誤差のような
ものです、多くの場合、気にする必要はありません。


「我々が日々目にする(あるいは構築する)アプリケーション」
はデスクトップアプリ・WEBアプリ等の処理速度を意識する場合でも
ミリ秒単位を考えれば多くの場合事足りる領域のソフトを指します。


「ヒープ領域」「スタック領域」に割り当てるコストが重大な問題になるのは
我々が日々目にする(あるいは構築する)アプリケーションではなく、もっと下層の
コンパイラ・OS・VM等においてです。これら下層においてメモリ消費やメモリ割当
に要する時間はかなり重要になってきます、その重要度は上層のアプリの比では
ありません。メモリ割当に関するコストがそれらの性能を左右するだけでなく、
それらを基盤とする上層のアプリの性能をも左右するからです。


ミリ秒単位よりミクロの単位で考える領域と秒単位(処理速度を気にする人でも
ミリ秒単位まで)を考えれば十分な領域を同じ土俵で論じること自体ナンセンスです。


ローカル変数はスタック領域を消費し、インスタンス変数はヒープ領域を消費する。
ヒープ領域の消費は後々問題になる場合が多いのでローカル変数を選択する
というのはにわか知識から導きだした結論以外のなにものでもありません。


メモリ割当に関して、にわか知識で判断するのは早計です。


我々が日々構築するアプリケーションでは処理速度優先で記述しておいて、
メモリ消費に関する問題が発生した場合は「ヒープ領域」「スタック領域」
どちらの領域で問題が発生しているか、どのオブジェクトのどのインスタンス
変数が問題となっているのか等を調査し、調査結果を突き合わせ、
対処法を考え、対策すればいいだけです。


何の問題も発生していない時点で将来問題になるかもしれないという一点で
最初から取る必要のない回避策を選択していくというのはどうかと思います。


VM系の言語では、インスタンス変数にするかローカル変数にするかについては
問題にならないケースが大半ですが、メモリに常駐するかのように振る舞う
オブジェクトの周囲等限定されたケースでは注意が必要です。


VM系の言語では、メモリに常駐するかのように振る舞うオブジェクトのインスタンス
変数から参照されるオブジェクトへの参照はいつまで経ってもきれません、参照される
オブジェクトはGCの対象とならずメモリ領域は解放されずに確保されつづけます。
メモリに常駐するかのように振る舞うオブジェクトの場合にはその周囲にインスタンス
変数による参照のチェインを形成しないように注意する必要があります。


ミクロの視点で「ヒープ領域」「スタック領域」を論じる前に、マクロの視点で
オブジェクトがメモリ上に時系列でどのように生成・破棄されているのかを
イメージできるようになるべき。


最初から「ヒープ領域」「スタック領域」夫々に割り当てた場合のコスト等を
意識してミクロの視点でクラスを設計するのではなく、最初はデータを管理
するのに適切なデータ構造、そのデータ構造で処理速度が最速になるロジック
をマクロの視点で考えクラスを設計すべき。


作成したロジックを実行し、求められる処理速度等の要件をクリアしない場合に
はじめて「ヒープ領域」「スタック領域」夫々に割り当てた場合のコスト等を
意識したミクロの視点を導入して要件に合致するようにクラスのデータ構造・
ロジックを修正・最適化していく。


マクロの視点で設計したクラスが要件を満たすのなら、ミクロの視点を導入する
ことは不必要。最初からミクロの視点を導入してクラスを設計するのは
マクロの視点で設計したクラスで要件を満たすケースでは不必要な最適化を
行っていることになる。


ミクロの視点を導入したクラス設計を最初から行うのは、マクロの視点で
設計したクラスが要件を満たさないと経験則等から判断できる場合に限るべき。


マクロの視点で問題点を整理してから必要であればミクロの視点に落とし込むのが
正しい思考法、最初からミクロの視点で考えるのは、幹を見ずに枝葉だけ見て
考えるようなもの。


推敲中。


Javaの場合、ローカル変数がオブジェクトを格納する場合、スタック領域に実際に
格納されるのはリファレンス(ポインタ)であり、オブジェクト本体は
ヒープ領域に格納される。ローカル変数がプリミティブを格納する場合のみ、
スタック領域にプリミティブ本体が格納される。(参照)


(おそらく、動的OO言語でも、ローカル変数がオブジェクトを格納する場合、
スタック領域に実際に格納されるのはリファレンス(ポインタ)であり、
オブジェクト本体はヒープ領域に格納される。)

C++の場合、メソッド内でメモリの動的確保(new)するとスタック領域に格納される
のはポインタ(アドレス)、生成したオブジェクトの本体はヒープ領域に格納される。
(参照)


上記の文章では組込み系のシステムについて考慮していません。
組込み系システムではCPU・メモリの制約が大きいので、
「ヒープ領域」「スタック領域」に割り当てるコストが
重大な問題になるはずです。
組込みJavaVMや組込み機器向けC++コンパイラによっては
オブジェクトをヒープ領域に格納しないモノがあるかもしれません。
(組み込みシステムについて門外漢なので「はず」「かも」と
しておきます。)
iPhone等の割と高機能なCPUかつ割と大容量なメモリを積んでいる
機器では標準的な組込み機器ほど「ヒープ領域」「スタック領域」
に割り当てるコストについて神経質になる必要はないと思われます。


[蛇足]
Java6.0でエスケープ解析(デフォルトでは無効?)が導入されていて、有効にした場合
「ローカル変数はスタック領域を消費し、インスタンス変数はヒープ領域を消費する。」
と一概にはいえなくなります。


#
#主旨が伝わりづらい文章になっていたので追記しました。
#
#重箱の隅(筆者にとってこの文章で筆者が言いたいこと(主旨)ではないので適当に
#書いている部分)をつついて他人の書いた文章を電波呼ばわりする馬鹿がいるので、
#修正していきます。
#


#
#kensir0uさん、コメントありがとうございます。
#Java等のVM系言語ではミクロ視点を意識する必要は99%なくマクロ視点のみで考えれば
#十分だと自分も考えています。
#この文章の前に書いた「OO言語における処理速度向上」に対して「ミクロ視点を
#意識しないのはおかしい」という批判(?)に読める文章を書いた人物がいたので、
#この文章では「マクロ視点での設計で要件を満たすなら、ミクロ視点で考える必要は
#ない」と述べています。
#「OO言語とメモリ割当」のOO言語はC++VM系ではない言語を含んでいるので
# ミクロ視点について触れているというのもあります。
#
#上の文章を読み返して、少し修正しました。
#