2012年12月17日月曜日

Unityメモリ管理「値型とスタック」

前回記事に続いてUnityメモリ管理についてUnity Gemsより翻訳を紹介したい:

------
http://unitygems.com/memorymanagement

値型とスタック


自動変数はautoで定義されていた。現在はC#で使用されないため、このキーワードを見たことがないかもしれない。昔utoはデフォルト変数を意味していた。
これら2行は全く同じ内容だ。
int variable = 10;
auto int variable = 10;
自動変数の機能はコールスタックに変数を割り当てることだ。スタックとは変数の積み重ねであり、例えばお皿の積み重ねるときのように一番上の皿を減らしたり増やしたりすることは出来るが、下の皿を減らしたり増やしたりすることが出来ない。下記の図を参照してほしい。

スタックはLIFO(Last-In-First-Out)と呼ばれ、その名前どおりに最後に入力したものを最初に出力する。

スタックの一番上が何であるか識別するために、プログラムはスタックポインタと呼ばれるものを用いて、CPUの変数によりスタック上の現在の位置を上図の黒い矢印のように管理する。変数を増やすと、スタックポインタをインクリメント(またはOSのメモリ管理手法によってはデクリメント)する。

スタックは関数コールのために用いられ、プログラム全体はUpdate関数を呼ぶメイン関数であり、それによって他の関数を呼ぶのであり、スクリプトで用いる変数は、意図的に指定しないかぎり、どれもスタック上に乗せられた自動変数となる。

スタックフレームという概念も理解する必要があり、これは単に現在の実行関数によりローカル割り当てされた変数の集合である。(また、戻り番地と呼ばれる、関数が戻った直後に実行される命令の番地、も含まれる)コンパイラはスタックフレームの終わりにてポインタを管理し、そして実際にこのポインタから負のオフセットを用いてローカル変数が占有するメモリを見つける。実際上の利点は、関数が戻った後ローカル変数により領域が使われたり割り当てされることがないことであり、次の関数コールのための領域を確保できることです。

もし再帰プログラム(自らをコールする関数)を記述した場合、後続のコールは全てのローカル変数を必要とし戻り番地はスタック上の領域が必要であるため、潜在的にスタックの領域が不足する可能性があり、またさらにもしコードのバグにより止まることなく自らを呼ぶルーチンの場合、スタックオーバーフローのメッセージが表示される。

このため自動変数の生存期間と有効範囲は同調する。変数は宣言から始まって、波括弧 { から次の波括弧 } まで生存する。もう少し具体的に補足する。
void Update(){
    int number = 10;
    Fct(number);
    print(number);
}

void Fct(int n){
    int inside=20;
    n = n + inside;
    print(n);
}
Update関数がコールされ、最初の命令は変数宣言である。スタックポインタはひとつ押し上げられ、値の数がメモリの番地に記憶される。

二つめの命令は関数コールだ。関数を呼ぶとき、プログラムは停止して関数のアドレスにジャンプして、プログラムの元の位置を取得するため、いくつかのデータがスタックに渡される。パラメータ(がある場合)は、元の値の複製をすることで、スタックに記憶させる。

変数の number は、関数で使用されるためにスタックに記憶されず、新しい変数 n が作成されてスタック上に記憶され変数に number が割り当てられる。

これにより変数 n が関数の中にあり、変数の number を保持するが、その number はFct()には存在しないため見ることが出来ず、参照されることがない。

関数のなかで新たな整数が宣言される。スタックは図で示したようなものとなる。(簡略化しているため注意)

関数の終わりまでの間に、n はprintされ値として30を保持する。関数の中で生成された全ての変数は破棄され失われる。それは n と inside を含む。これら変数はメモリにまだあるにも関わらず、単に無視されシステムによりもはや考慮されることはない。

Updateに戻って、number をprintするが、関数で呼ばれた変数そのものではなかく複製の値であるため、元の値のまま10である。もしUpdateに戻って参照しようとしても、スタックポインタは n の番地を失っていて、コンパイラは変数が有効範囲にないというエラーを表示させる。

ここまで自動変数の有効範囲を見てきて、変数が宣言された{}の中で定義されることが確認できた。この外では変数は存在せず、参照することが出来ない。これらの波括弧{}は関数、if 分岐、およびloopの有効範囲を示す。

生存期間とはプログラムの中で変数が存在する期間を意味し、有効範囲とはプログラムの中で変数を参照することが出来るゾーンを定義する。自動変数の場合、両方は共通点が多い。

生存期間と有効範囲


全ての値型変数はスタックに格納され、それらは:


  • int
  • unsigned
  • float
  • double
  • char
  • struct
  • bool
  • byte
  • enum
  • long
  • short


Cから継承したほとんど全ての型で、例外としてboolは過去存在しなかったぐらいだ。

自動変数にとって、生存期間はコンパイラにより管理される。宣言された有効範囲の終わりは、同時に変数の終わりとなる。プログラマはメモリのロケーションを後続のためにリリースすることを気にする必要がなく、自動的に行われる。

-----

んー、今回はUnityやC#に特化した話はなかったようにおもう。(あったとしても筆者のスキルでは気付けなかった・・・)

その意味では日本語で書かれた記事のほうが読みやすいかもしれない。参考までに同じようなテーマを扱っているITPro記事のリンクを紹介する。

ITPro記事(日経BP)
レジスタとスタックを使ってサブルーチン呼び出しを実行  
2007/06/05

プログラミングの基礎を積み上げていこうぜ!

0 件のコメント:

コメントを投稿

ブックマークに追加

このエントリーをはてなブックマークに追加

自己紹介

自分の写真
Unity3D公式マニュアル翻訳やってる人がスマホ(iPhone, Android)のゲーム開発しています。気軽に面白く初心者が遊べる内容がモットー。Blogでは開発情報をひたすら、Twitterではゲーム作成の過程で参考にしている情報を中心につぶやきます

ページビューの合計

過去7日間の人気投稿

ブログ アーカイブ