以下のようなWindows環境でメモリを割り当てるための方法はたくさんありますVirtualAlloc、HeapAlloc、malloc、new。
したがって、それらの間の違いは何ですか?
回答:
各APIはさまざまな用途に使用されます。また、メモリを使い終わったら、それぞれが正しい割り当て解除/解放機能を使用する必要があります。
多くのオプションを提供する低レベルのWindowsAPIですが、主にかなり特定の状況にある人々に役立ちます。より大きなチャンク(編集:4KBではない)にのみメモリを割り当てることができます。あなたがそれを必要とする状況があります、しかしあなたがこれらの状況の1つにいるときあなたは知っているでしょう。最も一般的なものの1つは、メモリを別のプロセスと直接共有する必要がある場合です。汎用のメモリ割り当てには使用しないでください。VirtualFree割り当て解除に使用します。
VirtualAlloc。よりも大きなチャンクではなく、必要なサイズのメモリを割り当てます。HeapAllocいつ呼び出す必要があるかを知っており、VirtualAlloc自動的に呼び出します。と同様mallocですが、Windowsのみであり、さらにいくつかのオプションがあります。一般的なメモリチャンクの割り当てに適しています。一部のWindowsAPIでは、これを使用して渡すメモリを割り当てるか、そのコンパニオンHeapFreeを使用して返されるメモリを解放する必要があります。
メモリを割り当てるCの方法。C ++ではなくCで記述していて、コードをたとえばUnixコンピューターでも機能させたい場合、または誰かが特にそれを使用する必要があると言っている場合は、これをお勧めします。メモリを初期化しません。のようなメモリの一般的なチャンクを割り当てるのに適していHeapAllocます。シンプルなAPI。free割り当て解除に使用します。Visual C ++のmalloc呼び出しHeapAlloc。
メモリを割り当てるC ++の方法。C ++で記述している場合は、これをお勧めします。1つまたは複数のオブジェクトを割り当てられたメモリにも配置します。delete割り当て解除(またはdelete[]配列)に使用します。Visual Studioのnew呼び出しはHeapAlloc、呼び出し方法に応じて、オブジェクトを初期化します。
最近のC ++標準(C ++ 11以降)では、手動で使用する必要がある場合delete、それは間違っており、代わりにのようなスマートポインターを使用する必要がありますunique_ptr。C ++ 14以降、同じことが言えますnew(などの関数に置き換えられましたmake_unique())。
SysAllocString特定の状況で使用する必要があると言われるような、他の同様の機能もいくつかあります。
メモリ管理を必要とする言語(CやC ++など)の使用を計画している場合は、メモリ割り当てAPI(Windows)の違いを理解することが非常に重要です。それを説明する最良の方法は、図を使用することです。
これは非常に単純化されたWindows固有のビューであることに注意してください。
この図を理解する方法は、図の上位にあるメモリ割り当て方法ほど、使用する実装のレベルが高くなることです。しかし、下から始めましょう。
オペレーティングシステムのすべてのメモリ予約と割り当て、およびメモリマップファイル、共有メモリ、コピーオンライト操作などのサポートを提供します。ユーザーモードコードから直接アクセスすることはできないため、スキップします。ここにあります。
これらは、ユーザーモードから利用できる最低レベルのAPIです。この関数は基本的にZwAllocateVirtualMemoryを呼び出し、ZwAllocateVirtualMemoryはクイックsyscallを実行して、カーネルメモリマネージャーにさらなる処理を委任します。また、ユーザーモードで使用可能なすべてのメモリから新しいメモリのブロックを予約/割り当てるための最速の方法です。VirtualAllocring0
ただし、主な条件は2つあります。
システムの粒度の境界に揃えられたメモリブロックのみを割り当てます。
システムの粒度の倍数であるサイズのメモリブロックのみを割り当てます。
では、このシステムの粒度は何ですか?GetSystemInfoを呼び出すことで取得できます。dwAllocationGranularityパラメータとして返されます。その値は実装(および場合によってはハードウェア)固有ですが、多くの64ビットWindowsシステムでは、0x10000バイトまたはで設定されます64K。
つまり、これが意味するのは、割り当てようとすると、次のように8バイトのメモリブロックだけを言うということですVirtualAlloc。
void* pAddress = VirtualAlloc(NULL, 8, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
成功した場合pAddress、0x10000バイト境界に揃えられます。また、8バイトのみを要求した場合でも、実際に取得されるメモリブロックは全体page(または4Kバイトのようなものです。正確なページサイズがdwPageSizeパラメータに返されます)になります。ただし、その上、メモリブロック全体が返されます。からのスパニング0x10000バイト(または64Kほとんどの場合)は、それ以上の割り当てには使用できpAddress ません。したがって、ある意味で、8バイトを割り当てることで、65536を要求することもできます。
したがって、ここでの話の教訓は、VirtualAllocアプリケーションの一般的なメモリ割り当ての代わりになることではありません。以下のヒープで行われるように、非常に特殊な場合に使用する必要があります。(通常、メモリの大きなブロックを予約/割り当てるため。)
VirtualAlloc誤って使用すると、深刻なメモリの断片化につながる可能性があります。
一言で言えば、ヒープ関数は基本的にVirtualAlloc関数のラッパーです。ここでの他の答えはそれのかなり良い概念を提供します。非常に単純な見方で、ヒープが機能する方法は次のとおりです。
HeapCreateVirtualAlloc内部で(またはZwAllocateVirtualMemory具体的に)呼び出すことにより、仮想メモリの大きなブロックを予約します。また、仮想メモリの予約済みブロック内でさらに小さなサイズの割り当てを追跡できる内部データ構造を設定します。
呼び出しHeapAllocとは、HeapFree(リクエストが既に予約されているものを超えて、もちろん、しない限り、実際に解放/任意の新しいメモリを割り当てていないHeapCreate)が、代わりに、彼らはメーターアウト(またはcommit小さいメモリブロックことにそれを解剖することにより、)以前に予約し大きな塊ユーザーが要求します。
HeapDestroy次にVirtualFree、実際に仮想メモリを解放する呼び出し。
したがって、これらすべてにより、ヒープ関数はアプリケーションでの一般的なメモリ割り当ての完全な候補になります。任意のサイズのメモリ割り当てに最適です。しかし、ヒープ関数の利便性のために支払う小さな代償は、VirtualAllocより大きなメモリブロックを予約するときにわずかなオーバーヘッドが発生することです。
ヒープのもう1つの良い点は、実際にヒープを作成する必要がないことです。通常、プロセスの開始時に作成されます。したがって、GetProcessHeap関数を呼び出すことでアクセスできます。
ヒープ関数の言語固有のラッパーです。、などとは異なりHeapAlloc、HeapFreeこれらの関数は、コードがWindows用にコンパイルされている場合だけでなく、他のオペレーティングシステム(Linuxなど)でも機能します。
これは、Cでプログラムする場合にメモリを割り当て/解放するための推奨される方法です(特定のカーネルモードのデバイスドライバーをコーディングしている場合を除く)。
高レベルの(まあ、C++)メモリ管理演算子として来てください。これらはC++言語に固有であり、のようmallocにC、heap関数のラッパーでもあります。またC++、コンストラクタの特定の初期化、デストラクタでの割り当て解除、例外の発生などを処理する独自のコードがたくさんあります。
これらの関数は、でプログラムする場合にメモリとオブジェクトを割り当て/解放するための推奨される方法ですC++。
最後に、VirtualAllocプロセス間でメモリを共有するために使用することについての他の応答で言われたことについて1つのコメントをしたいと思います。VirtualAllocそれ自体では、予約済み/割り当て済みのメモリを他のプロセスと共有することはできません。そのためCreateFileMappingには、他のプロセスと共有できる名前付き仮想メモリブロックを作成できるAPIを使用する必要があります。また、読み取り/書き込みアクセスのために、ディスク上のファイルを仮想メモリにマップすることもできます。しかし、それは別のトピックです。
VirtualAllocOS仮想メモリ(VM)システムの特殊な割り当てです。VMシステムでの割り当ては、アーキテクチャに依存する割り当ての粒度(割り当ての粒度)で行う必要があります。VMシステムでの割り当ては、メモリ割り当ての最も基本的な形式の1つです。VMの割り当てにはいくつかの形式がありますが、メモリは必ずしも専用であるとは限らず、RAMに物理的にバックアップされているとは限りません(可能ですが)。VMの割り当ては、通常、特別な目的のタイプの割り当てです。
HeapAlloc本質的にはmalloc、new両方が最終的に呼び出すものです。これは、非常に高速で、汎用割り当てのさまざまなタイプのシナリオで使用できるように設計されています。古典的な意味での「ヒープ」です。ヒープは実際にはによってセットアップされますVirtualAlloc。これは、OSから割り当てスペースを最初に予約するために使用されます。スペースがによって初期化された後VirtualAlloc、さまざまなテーブル、リスト、およびその他のデータ構造が、HEAPの動作を維持および制御するように構成されます。その操作の一部は、ヒープの動的なサイズ変更(拡大および縮小)、特定の使用法へのヒープの適応(あるサイズの頻繁な割り当て)などの形式です。
newとmalloc多少同じですmallocが、本質的にはHeapAlloc( heap-id-default );への正確な呼び出しです。newただし、[さらに] C ++オブジェクトに割り当てられたメモリを構成できます。特定のオブジェクトについて、C ++は各呼び出し元のヒープにvtablesを格納します。これらのvtableは実行用のリダイレクトであり、継承、関数のオーバーロードなどのOO特性をC ++に与えるものの一部を形成します。
他のいくつかの一般的な配分のような方法_alloca()と_malloca()されているスタックベース。FileMappingsは、実際にはVirtualAlloc、それらのマッピングをタイプに指定する特定のビットフラグで割り当てられ、設定されますFILE。
ほとんどの場合、そのメモリの使用と一致する方法でメモリを割り当てる必要があります;)。 newC ++、mallocC、VirtualAlloc大規模またはIPCの場合。
***によって行われた大容量のメモリ割り当てHeapAllocは、実際VirtualAllocにはある程度のサイズ(数百kまたは16 MBか、私が忘れているものですが、かなり大きいです:))の後に出荷されます。
***編集私はIPCについて簡単に述べましたが、この質問への回答者の誰も議論していないVirtualAlloc関連についても非常にきちんとしたものがありますVirtualAlloc。
VirtualAllocExは、あるプロセスが別のプロセスのアドレス空間にメモリを割り当てるために使用できるものです。最も一般的には、これを組み合わせて使用して、CreateRemoteThreadを介して別のプロセスのコンテキストでリモート実行を取得します(同様にCreateThread、スレッドは他のプロセスで実行されるだけです)。
概要:
VirtualAlloc、HeapAllocなどは、OSから直接さまざまなタイプのメモリを割り当てるWindowsAPIです。VirtualAllocはWindows仮想メモリシステムのページを管理し、HeapAllocは特定のOSヒープから割り当てます。率直に言って、これらのいずれかを使用する必要はほとんどありません。
mallocは、プロセスにメモリを割り当てる標準C(およびC ++)ライブラリ関数です。mallocの実装では、通常、OS APIの1つを使用して、アプリの起動時にメモリのプールを作成し、mallocリクエストを行うときにそこから割り当てます。
newは、メモリを割り当ててから、そのメモリでコンストラクタを適切に呼び出す標準のC ++演算子です。これは、mallocまたはOS APIの観点から実装できます。その場合、通常、アプリケーションの起動時にメモリプールが作成されます。
VirtualAlloc=>仮想メモリに直接割り当て、ブロック単位で予約/コミットします。これは、大きな配列などの大きな割り当てに最適です。
HeapAlloc/ new=>デフォルトのヒープ(または作成する可能性のあるその他のヒープ)にメモリを割り当てます。これはオブジェクトごとに割り当てられ、小さなオブジェクトに最適です。デフォルトのヒープはシリアライズ可能であるため、スレッドの割り当てが保証されています(これにより、高性能シナリオで問題が発生する可能性があるため、独自のヒープを作成できます)。
malloc=> Cランタイムヒープを使用しますHeapAllocが、互換性のシナリオでは一般的です。
簡単に言うと、ヒープは、(生の仮想メモリではなく)ヒープマネージャによって管理される仮想メモリのチャンクにすぎません。
メモリワールドの最後のモデルはメモリマップトファイルです。このシナリオは、データの大きなチャンク(大きなファイルなど)に最適です。これは、EXEを開くときに内部的に使用されます(EXEをメモリにロードせず、メモリマップファイルを作成するだけです)。