10

どれだけのメモリを割り当てることができるかを理解するために遊んでいます。当初、割り当て可能な最大メモリは物理メモリ(RAM)と同じだと思いました。以下に示すコマンドを実行して、Ubuntu12.04のRAMを確認しました。

~$ free -b
             total       used       free     shared    buffers     cached
Mem:    3170848768 2526740480  644108288          0  265547776 1360060416
-/+ buffers/cache:  901132288 2269716480
Swap:   2428497920          0 2428497920

上に示したように、物理メモリの合計は3Gig(3170848768バイト)で、そのうち644108288バイトだけが空いているので、最大でこれだけのメモリしか割り当てることができないと思いました。以下の2行だけで小さなプログラムを書いてテストしました。

char * p1 = new char[644108290] ;
delete p1;

コードは完全に実行されたため、メモリが正常に割り当てられたことを意味します。また、使用可能な物理空きメモリよりも多くのメモリを割り当てようとしましたが、エラーは発生しませんでした。その後、質問ごとに

mallocが割り当てることができる最大メモリ

仮想メモリを使用しているに違いないと思ったので、コードの空きスワップメモリ​​をテストしたところ、動作しました。

char * p1 = new char[2428497920] ;
delete p1;

空きスワップと空きRAMバイトのメモリを割り当てようとしました

char * p1 = new char[3072606208] ;
delete p1;

しかし、今回のコードはbad_alloc例外のスローに失敗しました。なぜ今回はコードが機能しなかったのですか。

次に、以下に示すように、コンパイル時に新しいプログラムでメモリを割り当てました。

char p[3072606208] ;
char p2[4072606208] ;
char p3[5072606208];
cout<<"Size of array p = " <<sizeof p <<endl;
cout<<"Size of array p2 = " <<sizeof p2<<endl;
cout<<"Size of array p2 = " <<sizeof p3;

出力ショー

Size of array p = 3072606208
Size of array p1 = 4072606208
Size of array p2 = 777638912

ここで何が起こっているのか理解するのを手伝っていただけませんか。コンパイル時にメモリを割り当てることができたのに、動的には割り当てられなかったのはなぜですか。コンパイル時間を割り当てると、どうしてpスワップp1と空きRAMメモリよりも大きいメモリを割り当てることができたのでしょうか。p2失敗したところ。これはどの程度正確に機能していますか。これは未定義の動作ですか、それともOS固有の動作ですか。ご協力いただきありがとうございます。Ubuntu12.04とgcc4.6.3を使用しています。

4

5 に答える 5

8

メモリページは、使用するまで実際にはプログラムにマップされません。ある範囲のmalloc仮想アドレス空間を予約するだけです。それらを読み書きしようとするまで、物理RAMはそれらの仮想ページにマップされません。

グローバルメモリまたはスタック(「自動」)メモリを割り当てた場合でも、それらに触れるまで、物理ページのマッピングはありません。

最後にsizeof()、コンパイラがOSが後で何をするかわからない場合、コンパイル時に評価されます。したがって、オブジェクトの予想サイズがわかります。

memsetそれぞれの場合にメモリを0にしようとすると、動作が大きく異なることがわかります。また、callocメモリをゼロにするを試してみることもできます。

于 2012-10-25T18:19:02.140 に答える
2

There's a huge difference between these two programs:

program1.cpp

int main () {
   char p1[3072606208];
   char p2[4072606208];
   char p3[5072606208];

   std::cout << "Size of array p1 = " << sizeof(p1) << std::endl;
   std::cout << "Size of array p2 = " << sizeof(p2) << std::endl;
   std::cout << "Size of array p3 = " << sizeof(p3) << std::endl;
}

program2.cpp:

char p1[3072606208];
char p2[4072606208];
char p3[5072606208];

int main () {

   std::cout << "Size of array p1 = " << sizeof(p1) << std::endl;
   std::cout << "Size of array p2 = " << sizeof(p2) << std::endl;
   std::cout << "Size of array p3 = " << sizeof(p3) << std::endl;
}

The first allocates memory on the stack; it's going to get a segmentation fault due to stack overflow. The second doesn't do much at all. That memory doesn't quite exist yet. It's in the form of data segments that aren't touched. Let's modify the second program so that the data are touched:

char p1[3072606208];
char p2[4072606208];
char p3[5072606208];

int main () {

   p1[3072606207] = 0;
   p2[3072606207] = 0;
   p3[3072606207] = 0;

   std::cout << "Size of array p1 = " << sizeof(p1) << std::endl;
   std::cout << "Size of array p2 = " << sizeof(p2) << std::endl;
   std::cout << "Size of array p3 = " << sizeof(p3) << std::endl;
}

This doesn't allocate memory for p1, p2, or p3 on the heap or the stack. That memory lives in data segments. It's a part of the application itself. There's one big problem with this: On my machine, this version won't even link.

于 2012-10-25T19:00:06.707 に答える
2

興味深い....注意すべきことの1つ:あなたが書くとき

char p[1000];

スタックに100バイトを割り当てます(まあ、予約します)。

あなたが書くとき

char* p = malloc(100);

ヒープに100バイトを割り当てます。大きな違い。[]の間の値がコンパイラによってintとして読み取られ、ラップアラウンドしてはるかに小さいブロックを割り当てない限り、スタック割り当てが機能している理由がわかりません。

ほとんどのOSはとにかく物理メモリを割り当てません。それらは、使用するまで未使用(したがって未割り当て)のままである仮想アドレス空間からページを提供します。その後、CPUのメモリマネージャユニットがメモリを提供します。を求めました。割り当てたバイトに書き込んでみて、何が起こるかを確認してください。

また、少なくともWindowsでは、メモリのブロックを割り当てる場合、OSが使用できる最大の連続ブロックのみを予約できます。そのため、割り当てを繰り返すことでメモリが断片化されるため、mallocで使用できる最大のサイドブロックが減少します。Linuxにもこの問題があるかどうかはわかりません。

于 2012-10-25T18:24:00.560 に答える
1

最初に注意すべきことは、最近のコンピューターでは、プロセスが(アプリケーションレベルで)RAMに直接アクセスできないことです。むしろ、OSは各プロセスに「仮想アドレス空間」を提供します。OSは、仮想メモリにアクセスするための呼び出しをインターセプトし、必要に応じて実メモリを予約します。

したがって、mallocまたはnewそれがあなたにとって十分なメモリを見つけたと言うとき、それは単に仮想アドレス空間であなたのために十分なメモリを見つけたことを意味します。次のプログラムをそのmemset行とコメントアウトして実行すると、これを確認できます。(注意してください、このプログラムはビジーループを使用します)。

#include <iostream>
#include <new>
#include <string.h>

using namespace std;

int main(int argc, char** argv) {

    size_t bytes = 0x7FFFFFFF;
    size_t len = sizeof(char) * bytes;
    cout << "len = " << len << endl;

    char* arr = new char[len];
    cout << "done new char[len]" << endl;

    memset(arr, 0, len); // set all values in array to 0
    cout << "done setting values" << endl;

    while(1) {
        // stops program exiting immediately
        // press Ctrl-C to exit
    }

    return 0;
}

がプログラムの一部である場合memset、コンピュータが使用するメモリが大幅にジャンプすることに気付くでしょう。それがないと、違いがあったとしてもほとんど気付かないはずです。呼び出されるとmemset、配列のすべての要素にアクセスし、OSに物理メモリ内のスペースを使用可能にするように強制します。newの引数はsize_tここを参照)なので、呼び出すことができる最大の引数はですが2^32-1、これが成功することは保証されていません(私のマシンでは確かにそうではありません)。

スタックの割り当てについて:David Hammemの答えは、私よりも優れていると言っています。あなたがそれらのプログラムをコンパイルできたことに驚いています。あなたと同じセットアップ(Ubuntu12.04およびgcc4.6)を使用すると、次のようなコンパイルエラーが発生します。

test.cpp:関数内'int main(int、char **)':

test.cpp:14:6:エラー:変数'arr'のサイズが大きすぎます

于 2012-10-25T19:54:13.250 に答える
0

次のコードを試してください。

bool bExit = false;
unsigned int64 iAlloc = 0;

do{
   char *test = NULL;
   try{
        test = new char[1]();
        iAlloc++;
   }catch(bad_alloc){
   bExit = true;}
}while(!bExit);

char chBytes[130] = {0};
sprintf(&chBytes, "%d", iAlloc);
printf(&chBytes);

1回の実行では、他のプログラムを開かないでください。他の実行では、メモリマップトファイルを使用するアプリケーションにいくつかの大きなファイルをロードします。

これはあなたが理解するのを助けるかもしれません。

于 2012-10-25T18:47:01.857 に答える