雑記
コンパイルの話:マシン語に落ちるということ
最終更新:
匿名ユーザー
-
view
C言語なりなんなりのソースファイルをコンパイルすると、まあ大体マシン語が吐き出される。
その過程において、コンパイラはいろいろ苦労している。前に書いた最適化、なんていうのもそう。
そのほかにも、例えば動的なメモリの確保、なんていうのもそうだ。
メモリは限られたリソースであり、無限に使えるわけじゃない。実行時には最大でもそのPCに乗せてある分しか使えないわけだ。いつ、どのプログラムがどれだけメモリ使うかなんてわからないのに。
なのに、ソースコード上では便利に
unsigned char* pHoge; pHoge = malloc((size_t)100);
なんてことができちゃう。お気軽お手軽。
こんなことができるのも、OSさんのおかげ。
「ちくしょうまたフリーズかよ」「だからWind○wsは」なんて言っては、いけないのである。
OSの重要な役割はたくさんあるけど、メモリの管理もやってくれているわけだ。
仕組みは、というと、以下はかなり簡略化した話だが、だいたいこうなる。
アプリケーションから「100byteメモリ欲しいよ」と言われたらまずOSが管理しているメモリプールのなかから100byte+αの領域があるかを探す。あったら、まずその領域のサイズやどのアプリケーションに割り当てたかなどの情報を割り当てるメモリの先頭に保存する。
つまり「誰になんぼ貸したか」を記録するわけだ。金貸しの因業ババアみたい。
そして、その情報の後ろの領域をアプリケーションに割り当てる。
つまり、
pHoge = malloc((size_t)100);
としたときにはpHoge[-XX]の領域には動的に割り当てた際の情報が入っている。
逆に開放する際にpHogeに割り当てた際の領域の先頭アドレスをもらうので、そこからアドレスをさかのぼって情報を手繰り、その情報を元にして開放の処理(再びメモリプールにもどす)を行う。
したがって、100byteの領域を10個確保すると、1000byte以上のメモリを使うことになる。
また、A、B、Cそれぞれ100byteづつ確保したとして、Bを解放、その後Dとして200byte確保しようとすると、Bがあいた隙間では足りないのでCの後ろに200byteを確保する。
したがって小さな領域の確保/解放を繰り返すと、メモリが「歯抜け」の状態になってしまう。当然、実際に確保している(必要としている)メモリ容量よりも随分大きなメモリが必要になってしまう。特に、画像などの大きなメモリを必要とする処理などが含まれる場合は顕著だ。
つまり、メモリの動的確保と言うのは
①いちいちOSにお伺いを立てる必要がある(オーバーヘッドがある)
②OSが管理するための要領の分、無駄がある
③メモリのフラグメンテーション(歯抜け状態)をおこしやすい
④追加:解放をうっかり忘れると目も当てられない
・・・という欠点がある。
①いちいちOSにお伺いを立てる必要がある(オーバーヘッドがある)
②OSが管理するための要領の分、無駄がある
③メモリのフラグメンテーション(歯抜け状態)をおこしやすい
④追加:解放をうっかり忘れると目も当てられない
・・・という欠点がある。
しかしながらファイルや文字列のサイズなんて実行時にならないとわからないことも多い。
なので、とりあえず妥協策。
大きなサイズのメモリ(ファイルなど)は動的に確保するとして、小さなサイズで必要なものは静的なバッファをもつ。例えば、グローバル領域などに256byteの領域をワーク領域として持っておく。
これを排他的に使えば、小さな領域を一時的に確保/解放する必要はなくなる。また、フラグメンテーションをおこしやすい小さな領域の確保/解放を避けられるので、一粒で二度おいしい。
メモリは、賢く使いましょう。