ゲームプログラミングの本やウェブ上の情報を見ていると、「メモリ管理」という項目が設けられていることが多々あるのですが、メモリ管理は具体的にどういうことをやることなのでしょうか?
内容が難しく抽象的で毎回何かしらの理解できないことがあって読み進められません。
ゲームプログラミング限定ではなく、組み込み系におけるメモリ管理など分野は何でもいいので、メモリ管理の簡単で具体的な説明をよろしくお願いします。
気になる質問をクリップする
クリップした質問は、後からいつでもMYページで確認できます。
またクリップした質問に回答があった際、通知やメールを受け取ることができます。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
回答6件
0
「メモリ管理」とだけ言われても文脈によってどういうことを指すのかが変わりますし、言語によっても違ってきますから、一言で説明するのは難しいですね。
- メモリの確保/解放を管理する。
いわゆる動的メモリ確保のことで、単にメモリ管理といった場合はこれを指す場合が多いと思います。必要なときに必要なサイズのメモリ領域を確保し、いらなくなったら解放するという仕組みですね。
C/C++ではmalloc/freeなどの関数が用意されていますし、C++にはnew/deleteという命令もあります。
Java/C#ではメモリ確保とはnewでオブジェクトをインスタンス化することですが、解放はガベージコレクションという仕組みで知らないところで勝手にやってくれるのでプログラマーがあまり意識することはないかもしれません。ただし、ガベージコレクションの仕組みを理解していないと、消費メモリが増えすぎたり、不定期にカクッと一瞬動きが止まったりなどということが起こるかもしれません。
C/C++の場合はメモリを直接いじれるという特性から、標準のメモリ管理機能を使わずに、より高速に動作する独自のメモリ管理機能を実装するということはよくあります。
- メモリを安全/確実に解放する。
これはC/C++では重要なテーマとなります。確保した領域を解放し忘れると「メモリリーク」となり、無駄にメモリを消費することになり、最悪システムが不安定になったりします。そうならないようにC++ではスマートポインタという仕組みが考案されていて、STLにもその機能が提供されています。
また、「コンテナ」とよばれるメモリの確保/解放を自動で行う機能を持ったクラスも提供されています。
- メモリ消費量を管理する。
最新のゲーム機はPC並のメモリ搭載量ですが、携帯ゲーム機や安いスマホなどはメモリ搭載量が少ないので、「メモリの無駄遣い」をしないように心がける必要があります。例えば画面上に表示できるキャラは○○体まで、などというような制限を付けて、メモリ消費を一定以下に収まるように管理します。その際、確保/解放とも絡めて、あらかじめ必要な分だけメモリを確保しておき、それを使い回すというテクニックもあります。
これはゲームだけでなく、メモリ搭載量に制限のある組み込み機器などでも重要なテーマです。
投稿2016/03/13 15:02
総合スコア5938
0
ベストアンサー
###クライアントでのメモリ管理
クライアント、つまりアプリ側でのメモリ管理というなら、やはりオブジェクトの生成と破棄になると思います。
例えばシューティングゲームで大量の弾を発射する度に、newしてメモリ確保して弾オブジェクトを生成して、敵に当るか画面外に消えたらdeleteして、なんて真似をしていたらすぐにガベージコレクションが走ってしまいます。
ガベコレの無い言語であっても、newしてメモリ確保する際のシステムコールは馬鹿にならない程思いです。
ゲームは60FPSや30FPS固定で動作することが前提で作られているものが多いです。
しかし、ガベコレが走ってしまったり、1フレーム内の処理が重くなってしまうとFPSを固定することができなくなります。
そうなるとフレーム落ちが発生してしまい、キャラが瞬間移動したり、当っているはずの弾が敵をすり抜けたり、とゲームとして成立しなくなってしまいます。
そうならないために、シューティングゲーム等では1度に表示する弾の数だけゲーム開始前に全てプールに確保してしまいます。
C#
1Queue<Bullet> pool = new Queue<Bullet>(); 2for(int i = 0; i < 1度に表示する最大数; i++) 3{ 4 pool.Enqueue(new Bullet()); 5}
そして、発射の際に先に確保しておいたプールの中から使っていないものを一つ取り出し、アクティブな状態にして表示させます。
C#
1Bullet bullet = pool.Dequeue(); 2bullet.実際に使えるようにする処理
敵に当ったり、画面外に消えるとそれを削除して、元のプールに戻します。
C#
1bullet.使い終わった状態にする処理 2pool.Enqueue(bullet);
こうすることで、ガベコレを防いだり1フレーム内のシステムコールをなるべく減らして処理を軽くすることでFPSを安定させます。
このように、ゲーム内では無限に湧いているように見える弾や敵キャラなども、1度確保したメモリを使いまわして実現しています。
###サーバでのメモリ管理
MMO等のサーバでもオブジェクトの生成、破棄をさせないで、ガベコレを防ぐという考え方は一緒ですが、もう一つ重要なメモリ管理があります。
それは1サーバで同時にログインするキャラ数分の、1キャラが消費するメモリをサーバ立ち上げ時に全て確保しないといけないということです。
1キャラが消費するメモリとは、そのキャラが持っているアイテム等のことです。
これを行っていないと、ログインは出来たけどアイテムで使用するメモリが確保できず、誰かがアイテムを使用してサーバのメモリ上から消えてくれない限り、アイテムを使用することができなくなってしまいます。
投稿2016/03/15 06:25
退会済みユーザー
総合スコア0
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
0
C 言語ですが、ずーっと昔に、 malloc がメモリ確保時に確保するヘッダの16バイト節約のために、自前のメモリ管理モジュールを作りました。
c
1 2struct list_t { 3 list_t *p, *n; 4 size_t size; 5 char rsv[10]; 6}; 716bit os なのでこれで16バイトかつ16バイトアラインメント。 8このヘッダの直後に、malloc で指定した大きさの16バイト整合した大きさのメモリが続きます。 9malloc(20); とか確保すると、ヘッダ含めて実際には48バイトの領域が取られ、未使用領域が分断されて、 1022バイトできます。この分断された使われない領域のために、細かいメモリ確保を繰り返すと実際のメモリ 11容量よりも少ないところで out of memory が出たりします。 12 13 なので、一度に確保可能な最大を予め確保し、自前のメモリ管理で管理しました。 14struct list_st { 15 list_st *p,*n; 16 size_t size; 17 char data[0]; 18}; 19アラインメントは、dword つまり4バイト整合とし、char data[0] が 4nの可変サイズになります。 20gcもどきとして、再利用キュー、my_malloc 時は、再利用キューに指定サイズ以上の領域があれば、 21そこから、無ければ大きい場所から新しく切り出し、my_free時は、再利用キューに登録して終わり。 224n 整合なので 下2ビットは無視できるので、一度に確保可能な最大確保量は、256kb になります。
誰でも少ない資源を効率良くと考えると行き着く先はこんな感じになるかと。昔はos のメモリ管理情報が
公開されてたので細かいメモリ確保、開放が頻繁な場合、こういう手を独自実装で高効率化を考える人は
自分以外にもいたはずです。
p,n は双方向リストです。
投稿2016/03/16 09:08
総合スコア1693
0
昔の体験談になります。
◆断片化を小さくに抑えるため、インスタンスの生成順とは逆順に開放を行う。
たとえばアクション ゲームで
- ステージ
- メインプレーヤ
- 敵プレーヤ
の順に読み込んでいる場合、
ステージ移動の際に前のステージを開放を行いして新しいステージを読み込んだ際に、
新しいステージの方が大きかった場合、
前のステージの領域は使われず、後ろのほうに確保されます。
この状態が繰り返されて使われない領域が増えていく状態が断片化になります。
この断片化を防ぐため、
・読み替えの頻度の低い順に読み込む様にする
・最初に読み込んだ項目を読み直す際、後に読み込んだ項目も開放して読み直す。
を行いました。
尚、最近のJava VMなどではメモリ コンパクション機能があるため、
メモリリークだけでなく、断片化に対しても効果が期待できます。
が、配列は連続したメモリ領域が必要になるのは今でも変わらないため、
特にバイナリを読み込む際には依然注意が必要です。
◆メモリを複数実装しているハード
どのメモリに確保するのかを指定することが出来たハードだったため、
Aのメモリに何を読み込むか
Bのメモリに何を読み込むか
それぞれのメモリの領域を超過しないか
などを計測・調整を行いました。
◆外部モジュール化
実行ファイルもメモリに読み込まれ実行されます。
巨大なプログラムでは、それだけメモリを圧迫することになりますが、
処理の中必要になるのはなかなか一部になります。
そのため、外部モジュールにして動的に読み分け、
リソースと同様にプログラム自体も必要なものだけ読み込む様にします。
Javaではクラスローダーなどで実現している機能の中では、
この効能を期待しているものが今でもあると思います。
◆リソースのライフサイクルの管理
JavaなどのGC系言語でもメディアリソース(画像)はGC対象外であることがあります。
またメディアリソースは容量もでかく、メモリを圧迫しやすいですが、
読み込み時間も長くなるため、先読みを行い長い間使ったりします。
リソースの内容をいくつか分類して、メモリが枯渇しない程度に、
頻繁に再読み込みが発生しない様にライフサイクルを調整行います。
以上、一部脱線しているものもあるかと思いますが、昔の体験談になります。
投稿2016/04/07 16:58
退会済みユーザー
総合スコア0
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
0
もしメモリが管理されていなかった場合PCは手当たり次第メモリにデータを書き込むことになり書きむ場所に保存されているデータが存在していた場合そのデータは消えてしまいますよね。
これを防ぐために保存するデータのメモリをロックしておくわけです。静的な型付け言語では,あらかじめ実行前に使うメモリの容量が解っている静的なデータはプログラムが実行される前にコンパイラがメモリを確保できるか調べて確保してくれますが,プログラムが実行する人の操作でデータのサイズが変わるような動的なデータはあらかじめどれくらいのメモリを使うかということをプログラムが判断することができません。
なので,メモリのアドレスを保存しておける領域を作って,データが生み出される時に確保されたメモリのアドレスの値を保存することで管理しているわけです(Cはこのメモリのサイズもデータが生み出される時にデータのサイズを計算してmallocという関数を使ってメモリを確保しなければいけませんでした)。
しかし,この保存したアドレスの値を消去したところで,そのアドレスが指し示しているメモリの場所は明示的に解放されないのです。もしこのメモリが解放されていないまま,プログラムを回し続けるともう使われていないデータが蓄積されてしまい,色々よくないことが起きます(例えばメモリの使用率が100%を超えるとプログラムがクラッシュして実行中の処理が止まったりします)。なのでメモリを解放する処理をプログラムに書く必要があり,どのタイミングでメモリを解放すべきかということの理解がコンピュータのパフォーマンスを考える上で重要なのです。
メモリの解放をいちいちコードに書くのは面倒なので,最近のプログラミング言語は自動でメモリを解放してくれる機能がついているようなものもありますが,言語仕様でメモリがどのように確保され解放されるか知っておくことは,それをイメージしながらプログラミングすることで,無駄がなく早いコードを書くことにもつながります。
基本的にオブジェクトのインスタンスは動的メモリ確保をするデータだと思ってください(何個生み出されるか実行前にわからないため)。
ゲームプログラミングはこのオブジェクトのインスタンスをよく使うことになり,それぞれを使う時には,そのオブジェクトが他のオブジェクトに使用されているかどうかという参照カウントという値を使用することが多いです。これはスマートポインタという仕組みがよく使われます。オブジェクトが生み出されて使われるごとに参照カウントを+1して,使い終わると値を-1減少させます。参照カウントが0になったらオブジェクトのデストラクタが起動するという仕組みになっています。
メモリの自動解放はこのような単純な仕組みになっていますのでメモリが解放できずに困った時は,自分で書いて直したりパフォーマンスを上げることもできるようになります。
投稿2016/03/13 14:06
編集2016/03/13 14:24退会済みユーザー
総合スコア0
0
こんにちは。
確かにググッてみたら、中々ハードな説明が多いですね。
C言語の場合、malloc()でメモリを獲得してfree()で開放することはご存知ですか?
そのmalloc()やfree()の中の人がやっていることがメモリ管理です。
投稿2016/03/13 13:43
総合スコア23272
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2016/03/13 14:50
2016/03/13 14:56
2016/03/13 15:15
2016/03/13 15:29
2016/03/13 15:57
2016/03/14 01:56 編集
2016/03/16 14:02
あなたの回答
tips
太字
斜体
打ち消し線
見出し
引用テキストの挿入
コードの挿入
リンクの挿入
リストの挿入
番号リストの挿入
表の挿入
水平線の挿入
プレビュー
質問の解決につながる回答をしましょう。 サンプルコードなど、より具体的な説明があると質問者の理解の助けになります。 また、読む側のことを考えた、分かりやすい文章を心がけましょう。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2016/03/13 15:32
2016/03/13 16:00