あなたの質問に対して、オペレーティングシステムに関する本の少なくともいくつかの章を簡単に書くことができると思います。タネンバウム:現代のオペレーティングシステムを読むことをお勧めします。
ヒープとスタックの主な違いは、1つはプロセス項目ごと、もう1つはスレッド項目ごとです。プログラムが開始されると、最初は最小限のヒープとスタックセグメントが取得されます。ヒープは大きくなり、スタックは静的になります(スレッドごとに)。終了しない再帰関数(無限再帰)を作成すると、スタックオーバーフローが発生します;)関数呼び出しでは、スタックセグメントにスタックフレームがあり、関数が終了すると、スタックは巻き戻され、フレームは自由に使用できます。次の関数。スタックは連続的な線形構造です。Linuxでは、環境変数を介してプロセスのスタックセグメントサイズを構成できます。Windows(少なくともMS Visual C ++では)では、スタックセグメントのサイズでリンカーフラグを渡すことができます。コンパイル時に大きな配列を割り当てると、スタックオーバーフローが発生する可能性もあります。
char test[1000000];
ヒープは別の話です。プロセスが起動するとき、ヒープサイズはデフォルト値であり、OSごとに、またはそのOSで使用されている構成によって異なる場合があります(たとえば、Windowsでは、私が覚えている限り、デフォルトで2MBです)。さらに、より多くのヒープが必要な場合、変数などにより多くのスペースを割り当てるために、ヒープは大きくなります。プログラムがヒープメモリを解放しない場合、プログラムはそれ(またはヒープスペース)を使い果たします。ヒープ実装にはさまざまなデータ構造があり、それらのいくつかは二分木派生物であり、いくつかはフィボナッチヒープ(木の森)などではありません。メモリアロケータの書き方についての記事などを読むことができます。これらのデータ構造は、割り当てられたチャンクの割り当てを解除する必要がある場合にヒープノードを検索するため、または新しいヒープスペースが必要な場合に追加(空きチャンクを検索)するために最適化する必要があります。
32ビットOSの各プロセスには、4GBの仮想アドレス空間があります。ご想像のとおり、4GBの仮想アドレス空間を持つすべてのプロセスが収まるRAMはそれほど多くありません。OSメモリはページで構成されており、不要になったときや期限切れになったときにHDにスワップされます。ここで、ページングが機能します。すべてがページにマップされます:スタックまたは成長するヒープを持つプロセス。動的に成長するヒープの構造により、複数のページに配置できます。これがヒープアクセスが非常に高くつく理由です。ページがメモリにない場合、ページフォールトが発生し、OSがディスクからページをロードする必要があるためです(これは大幅に遅くなる可能性があります)。実行中のスレッドのスタックフレームはプロセッサキャッシュにあり、RAMよりもはるかに高速です。
さまざまなヒープタイプが可能です。小さなオブジェクトに対して非常に高速なヒープや、マルチスレッド環境で非常に効率的なヒープが存在する場合があります。Alexandrescuは、「Modern C ++ Design」で、小さなオブジェクトのアロケータと小さなオブジェクトを管理するヒープを開発する方法について説明しています。この実装は、彼のLokiC++ライブラリで利用できます。一部の組み込みシステムは、物理的に異なるメモリ領域を提供し、さまざまなヒープタイプを上に実装できます。コンパイラを打ち負かしたいのであれば、独自のアロケータ(ヒープマネージャなど)を作成するのは大変な作業です。
よろしく、
Ovanes