同様に空きメモリが不足すると失敗しますか、malloc
それとも他の理由が考えられますか?
5 に答える
割り当て関数 ( malloc
、realloc
、calloc
、および POSIX ではposix_memalign
) のいずれかが、次のいずれかの理由で失敗する可能性があります。
- 仮想アドレス空間全体、または少なくともその使用可能な部分を使い果たしました。32 ビット マシンでは、4GB 相当のアドレスしかなく、おそらく 1GB ほどが OS カーネルで使用するために予約されています。マシンに 16GB の物理メモリがある場合でも、1 つのプロセスがアドレスを超えて使用することはできません。
- 仮想アドレス空間を使い果たしたわけではありませんが、断片化がひどく、要求されたサイズの連続した範囲のアドレスを使用できません。これは (32 ビット マシンで) 6 つの 512MB ブロックを正常に割り当て、1 つおきに解放してから 1GB ブロックを割り当てようとした場合に発生する可能性があります。もちろん、メモリ サイズが小さい例は他にもたくさんあります。
- あなた自身のプログラムがすべてを使用したか、マシン上で実行されている他のプログラムがすべてを使用したため、マシンの物理メモリが不足しています。一部のシステム (デフォルト構成の Linux) はオーバーコミットします。つまり
malloc
、この状況で失敗することはありませんが、代わりに、OS は後で 1 つまたは複数のプログラムを強制終了し、十分な物理メモリがないと判断します。ただし、堅牢なシステム (オーバーコミットが無効になっている Linux を含む) では、malloc
物理メモリが残っていないと失敗します。
厳密に言えば、割り当て関数はいつでも何らかの理由で失敗することが許されていることに注意してください。失敗を最小限に抑えることは、実装の品質の問題です。オブジェクトのサイズを縮小realloc
しても、失敗する可能性もあります。これは、割り当てをサイズごとに厳密に分離する実装で発生する可能性があります。もちろん、この場合、単純に古い (より大きな) オブジェクトを引き続き使用できます。
realloc
次のように作業していると考える必要があります。
void *realloc(void *oldptr, size_t newsize)
{
size_t oldsize = __extract_size_of_malloc_block(oldptr);
void *newptr = malloc(newsize);
if (!newptr)
return 0;
if (oldsize > newsize)
oldsize = newsize;
memcpy(newptr, oldptr, oldsize);
free(oldptr);
return newptr;
}
実装はそれよりも効率的に特定のケースを実行できる場合がありますが、示されているとおりに正確に機能する実装は 100% 正しいです。つまり、失敗したrealloc(ptr, newsize)
場合はいつでも失敗する可能性malloc(newsize)
があります。特に、割り当てを縮小している場合でも失敗する可能性があります。
現在、最新のデスクトップ システムでは、障害から回復しようとせずmalloc
、代わりに、失敗した場合にプログラムをすぐに終了malloc
する関数 (通常は と呼ばれる) でラップするという強力なケースがあります。当然、同じ議論が にも当てはまります。ケースは次のとおりです。xmalloc
malloc
realloc
- デスクトップ システムはしばしば「オーバーコミット」モードで実行されます。このモードでは、プログラムが実際にそのすべてを使用するわけではないと仮定して、カーネルが RAM + スワップによってバックアップできるよりも多くのアドレス空間を喜んで分配します。プログラムがすべてを使用しようとすると、強制終了します。そのようなシステムでは、 malloc はアドレス空間を使い果たした場合にのみ失敗します。これは 32 ビット システムではほとんどなく、64 ビット システムではほぼ不可能です。
- オーバーコミット モードでなくても、デスクトップ システムには非常に多くの RAM とスワップが利用できる可能性が高いため、
malloc
失敗するずっと前に、ユーザーはディスクのスラッシングにうんざりし、プログラムを強制終了します。 - 割り当ての失敗からの回復をテストする実用的な方法はありません。どの呼び出しが失敗したかを正確に制御できる shim ライブラリがあったとしても
malloc
(このような shim は、OS によっては作成するのがせいぜい難しく、最悪の場合は不可能です)、2 N 個の失敗パターンの順序をテストする必要があります。ここで、N は次のとおりです。プログラムで malloc を呼び出した回数。
引数 1 と 2 は組み込みシステムまたはモバイル システムには (まだ!) 適用されませんが、引数 3 はそこでも有効です。
引数 3 は、すべての呼び出しサイトで割り当ての失敗をチェックして伝播する必要があるプログラムにのみ適用されます。意図されたとおりに (つまり、例外を除いて) C++ を使用できる幸運な場合は、コンパイラに依存してエラー回復パスを作成できるため、テストの負担が大幅に軽減されます。そして、今日使用する価値のある高水準言語には、例外とガベージ コレクターの両方があります。
私はそれを主に実装固有だと言います。一部の実装は、失敗する可能性が非常に高い場合があります。再割り当てが失敗する前に、プログラムの他の部分が失敗する場合があります。常に防御して、失敗するかどうかを確認してください。
そして、再割り当てしようとした古いポインターを解放することを忘れないでください。
ptr=realloc(ptr,10);
常にメモリリークの可能性があります。
常に次のようにします。
void *tmp=ptr;
if(ptr=realloc(ptr,10)==NULL){
free(tmp);
//handle error...
}
2 つの質問があります。
malloc
または失敗する可能性realloc
は、ほとんどの最新のシステムでは無視できます。これは、仮想メモリが不足している場合にのみ発生します。システムは、メモリの予約ではなく、メモリへのアクセスに失敗します。
Wrt の失敗realloc
とmalloc
ほぼ同じです。さらに失敗する可能性がある唯一の理由realloc
は、不適切な引数を指定したことです。つまり、割り当てられていないか、以前に割り当てられていたmalloc
メモリrealloc
ですfree
。
編集: R.のコメントを考慮して。はい、割り当て時に失敗するようにシステムを構成できます。しかし、まず第一に、これはデフォルトではありません。そのように構成するには特権が必要であり、アプリケーション プログラマーとしてこれは当てにできるものではありません。第二に、システムがそのように構成されている場合でも、利用可能なスワップ領域が使い果たされた場合にのみエラーが発生します。通常、あなたのマシンはそれよりずっと前に使えなくなります: ハードディスク上で機械的な計算を行っています (AKA スワッピング)。