メモリ領域の情報取得
2007年09月14日 18時40分00秒 | メモリ関連
確保後のメモリ領域の情報を取得するにはGlobalFlags()関数を使います。(戻る)

プロトタイプ宣言

UINT GlobalFlags(
  HGLOBAL hMem  // グローバルメモリのハンドル
);
サンプル

// メモリ領域の情報取得
UINT uFlags;

if ( (uFlags = GlobalFlags(hMem)) != GMEM_INVALID_HANDLE ){
  BYTE bLockCount = LOBYTE( LOWORD(uFlags) );  // GMEM_LOCKCOUNT定数の論理積でも可能
  BYTE bDiscardrd = HIBYTE( LOWORD(uFlags) );
}
else{
  // エラー(無効なハンドル、破棄ハンドルの場合)
}
bLockCountにはロックカウント値を保持します。なお、GMEM_FIXED付きで確保されたメモリ領域は常に0です。
bDiscardrdには破棄ハンドルのフラグを保持します。なお、GMEM_DISCARDED定数があれば破棄ハンドルを表す。



--------------
	物理メモリ容量の取得(2)
2007年09月14日 18時41分16秒 | メモリ関連
4Gバイト以上の物理メモリ容量を取得するにはGlobalMemoryStatusEx()関数を使います。
搭載容量と空き容量が取得できます。使用容量は引き算して求めます。

// プロトタイプ宣言
BOOL GlobalMemoryStatusEx(
  LPMEMORYSTATUSEX lpBuffer  // メモリステータスの構造体
);

// 構造体
typedef struct _MEMORYSTATUSEX {
  DWORD dwLength;                     // 構造体のサイズ
  DWORD dwMemoryLoad;                 // メモリの使用率(%)
  DWORDLONG ullTotalPhys;             // 物理メモリの搭載容量
  DWORDLONG ullAvailPhys;             // 物理メモリの空き容量
  DWORDLONG ullTotalPageFile;         // ページングの搭載容量
  DWORDLONG ullAvailPageFile;         // ページングの空き容量
  DWORDLONG ullTotalVirtual;          // 仮想メモリの搭載容量
  DWORDLONG ullAvailVirtual;          // 仮想メモリの空き容量
  DWORDLONG ullAvailExtendedVirtual;  // 仮想メモリの拡張空き容量
} MEMORYSTATUSEX, *LPMEMORYSTATUSEX;

// 情報収集
MEMORYSTATUSEX msex = { sizeof(MEMORYSTATUSEX) };
GlobalMemoryStatusEx( &msex );

// 物理メモリの使用容量
DWORDLONG ullUsed = (msex.ullTotalPhys - msex.ullAvailPhys);
// 物理メモリの空き容量
DWORDLONG ullFree = (msex.ullAvailPhys);
// 物理メモリの搭載容量
DWORDLONG ullSize = (msex.ullTotalPhys);

注意事項:
(1)この関数は Windows 95/98/Me/CE の環境では使えません。
(2)この関数は Windows NT/2000/XP/Vista の環境で利用できます。





◆se_sonsonさん2009/10/2116:11:57
new、deleteした後の仮想メモリーの空き容量
WinXP,VC6で開発しています。

newを繰り返して大量のメモリーを取得し、
メモリー不足でこれ以上取得できなくなったところでそれらをdeleteします。
その後、大きなメモリーをmallocしようとしても、確保できません。

メモリーの断片化を疑って、GlobalMemoryStで、各所における空きメモリーの状況を調べると、
dwAvailPhys、dwAvailPageFileは大量メモリー確保前とほぼ等しい値に戻っているのですが、
dwAvailVirtualが、deleteする前と変わりません。
タスクマネージャーで見る「仮想メモリサイズ」は、deleteすると小さくなるのですが。

一体何が起こっているのでしょうか。

ちなみに、環境は以下の通り
CPU:Core2Quad(2.34G)
メモリ:2G
ページングサイズの割り当て:2046M

問題のプロセスがメモリー確保に失敗する時のメモリー使用量は、
(タスクマネージャ)
仮想メモリサイズ:2,022,192k
メモリ使用量:1,526,568k
(GlobalMemorySt)
物理メモリ空き状況 3997696 / 2146476032 [99%]
ページファイル空き状況 1055342592 / -164311040
仮想メモリ空き状況 22499328 / 2147352576

delete後は
(タスクマネージャ)
仮想メモリサイズ:50304k
メモリ使用量:24308k
(GlobalMemorySt)
物理メモリ空き状況 1651183616 / 2146476032 [23%]
ページファイル空き状況 -1218760704 / -164311040
仮想メモリ空き状況 22433792 / 2147352576

です。よろしくお願いします。

補足
さらに詳しく書かせていただきます。

メモリーの確保→開放の後は、malloc、newどちらでも失敗します。

また、その後確保したいメモリーの大きさは2M程度で、
確保→開放の量が少なければ(限界に達しなければ)、問題なく確保出来ています。

よろしくお願いします。

共感した 0
閲覧数:2,688 回答数:2 お礼:50枚 違反報告
ベストアンサーに選ばれた回答
 プロフィール画像
magurogetさん 編集あり2009/10/2222:17:44
・・・
(略)
・・・

わたしの環境でも失敗しました。
===
Microsoft Windows XP
Home Edition
Version 2002
Service Pack 3
Dell Inspiron I6000 Intel(R) Pentium(R) M
processor 1.86GHz
1.86 GHz, 1.00 GB RAM
物理アドレス拡張
===
…プログラムおよび実行例は略…
===
しかし、これでは、new が失敗したときにエラーを返すこと、delete で解放できること、にあまり意味がなくなってしまいますね。。。
まさか、delete が解放された隣接領域のマージ処理をしていない、などは考えられませんものね、たぶん。



マージ処理していないような気がしますね。以下のプログラムなら成功しました。
===
#include 
#include 
#include 

struct Chunk {
static const std::size_t size = 1000;
unsigned char mem[size];
Chunk *next;
Chunk(Chunk *n_ = 0) : next(n_) {}
};

int main()
{
std::cerr << "start\n";
const std::size_t n = 2000 * 1000;
char *c = new(std::nothrow) char[n];

Chunk head;
while (Chunk *p = new(std::nothrow) Chunk(head.next)) head.next = p;
std::cerr << "short of memory\n";

while (Chunk *p = head.next) {
head.next = p->next;
delete p;
}

delete[] c;
std::cerr << "deleted\n";

c = new(std::nothrow) char[n];
std::cerr << (c ? "OK" : "NG") << '\n';
delete[] c;

std::cerr << "finish\n";
}
===

なので、iespyadさんが紹介してくれている _heapmin() というのを呼べばうまくいく可能性が高いですね。
たぶん、UNIX の sbrk() などと似たような機能なのでしょうから。

しかし、マージ処理をしていないのがほんとうだとすれば、Microsoft の vc++ で、ライブラリ内でマージ処理をしないのは高速化のためなのでしょうかね。。。昔、読んだ UNIX 系のシステムならライブラリ内でマージ処理していたような記憶があるのですが、メモリ容量が少なかった時代だったからでしょうか。




> なので、iespyadさんが紹介してくれている _heapmin() というのを呼べばうまくいく可能性が高いですね。

わたしが試したところでは、残念ながら、だめでした。。。
ということで、delete が開放された領域の隣接マージをしないとすれば、以前、確保したサイズ以下なら delete が有効なはずなので、


「Windows では、制限いっぱいのメモリを使う場合、小さいメモリ領域を細切れに取るな。そうしたいなら、自分でメモリ管理しろ(大きな領域を new して、自プログラム内で細切れに使うなりなんなりしろ。自作のメモリ管理ルーチンを書け)」


ということなのでしょうか^^; C++ なら operator new() や operator delete() を名前空間(大域名前空間も含む)やクラスごとに定義できるので、C よりは楽かもしれませんが、そういう余分な処理を書かないといけないというのも何か馬鹿らしいですよね。。。


たとえば、以下の URL の記事を読むと、

http://itpro.nikkeibp.co.jp/article/COLUMN/20070816/279802/?ST=deve...

HeapFree() が内部でどういう処理をしているのかが問題なんだろうと思いますけど、内部処理についてはぱっと見たところでは書かれていませんし、msdn の HeapFree の項目を見ても言及されていないような感じです。

HeapAlloc() などが内部で、VirtualAlloc() を呼ぶであろう際の割り当て単位の中ではマージ処理を行っているという可能性もあるので、以下のコードならうまくいくかもしれませんね。つまり、「マージ処理範囲を大きくとらせるために、プログラム開始時点で、大きなサイズを確保して、すぐに解放しておく」ということをしてやる(試していないので、推測の域を出ませんけど。。時間があったら試してみます…^^)。
===
#include 
#include 
#include 

struct Chunk {
static const std::size_t size = 1000;
unsigned char mem[size];
Chunk *next;
Chunk(Chunk *n_ = 0) : next(n_) {}
};

int main()
{
std::cerr << "start\n";

const std::size_t progMaxSize = 100 * 1000 * 1000;
if (char *v = new(std::nothrow) char[progMaxSize]) {
std::cerr << "preserve maximum block allocated\n";
delete[] v;
std::cerr << "preserve maximum block deallocated\n";
} else {
std::cerr << "alloc failed\n";
return 1;
}

Chunk head;
while (Chunk *p = new(std::nothrow) Chunk(head.next)) head.next = p;
std::cerr << "short of memory\n";

while (Chunk *p = head.next) {
head.next = p->next;
delete p;
}
std::cerr << "deleted\n";

const std::size_t n = 2000 * 1000;
char *c = new(std::nothrow) char[n];
std::cerr << (c ? "OK" : "NG") << '\n';
delete[] c;

std::cerr << "finish\n";
}
===

もし、有償の処理系をお使いなら、Microsoft に問い合わせてみて、結果がわかったら教えてください(笑)

ナイス 0
違反報告
 プロフィール画像
質問した人からのコメント2009/10/23 11:05:19
大変丁寧に調べていただき、ありがとうございました。

結局メモリーを完全に開放して、再度確保することは出来なかったのですが、
メモリー不足で確保できない例外が発生する前であれば、解放と再度確保が出来ることを確認しましたので、
使用率を調べて、対応することにしました。

非常に役立つツールを教えて頂いたiespyadさんにも感謝です。
シェア
ツイート
はてブ
知恵コレ
Yahoo!検索で調べてみよう
タスクマネージャー 物理メモリ仮想メモリ 不足メモリ 割り当てメモリ 空き容量環境 状況
このQ&Aで解決しましたか?
質問する
閉じる
ベストアンサー以外の回答1〜1件/1件中
 プロフィール画像
iespyadさん 2009/10/2119:13:26
_heapmin()(HeapCompact())するとどうなる?
6の頃からずっとランタイムはHeap APIにほぼ丸投げだから
あとデバッグビルドしてないよね

断片化を疑うならSysinternalのVMMapで視覚的に確認できる