web-dev-qa-db-ja.com

スタックの使用量は多すぎますか?

最近、CまたはC++を記述しているときは、Javaとは異なり、オプションであるため、すべての変数をスタックで宣言します。

しかし、スタックで大きなものを宣言することは悪い考えだと聞いたことがあります。

  1. なぜこれが正確なのですか?私はスタックオーバーフローが関係していると考えていますが、なぜそれが起こるのかははっきりしていません。
  2. スタックにあるものは多すぎますか?

私は100MBのファイルをスタックに配置しようとしているのではなく、文字列バッファとして使用するために数十キロバイトの配列を配置しているだけです。スタックの使用量が多すぎませんか?

(重複している場合は申し訳ありませんが、スタックを検索すると、スタックオーバーフローへの参照が引き続き表示されます。呼び出しスタックタグすらなく、抽象タグを使用しました。)

26
Elliot Way

それはあなたのオペレーティングシステムに依存します。 Windowsでは、スタックの一般的な最大サイズは1MBですが、典​​型的な最近のLinuxでは8MBですが、これらの値はさまざまな方法で調整できます。コールスタック全体のスタック変数の合計(戻りアドレス、スタックベースの引数、戻り値のプレースホルダー、アライメントバイトなどの低レベルのオーバーヘッドを含む)がその制限を超えると、スタックオーバーフローが発生し、通常、回復のチャンスがないプログラム。

通常は数キロバイトで問題ありません。数十キロバイトは合計し始めるので危険です。数百キロバイトは非常に悪い考えです。

22
Sebastian Redl

唯一の有効な答えは曖昧です:「多すぎるとスタックがオーバーフローします。」

プログラムのエントリポイントと問題の関数との間のすべてのコード行の実装を完全に制御できない限り、使用可能なスタックの量を想定することはできません。たとえば、この関数を呼び出してもスタックオーバーフローが発生しないことは保証できません。

_void break_the_camels_back()
{
    int straw;
    ...
}
_

現代のUnixのデフォルトの8 MiBスタックは、スタックが進むにつれてかなりの余地があります。特に、8ビットのスタックポインターを備えたCPUを覚えるのに十分な私のような人にとっては。実際には、試さずに吹き飛ばすことはほとんどありません。その場合、通常、スタック制限を超えるとセグメンテーション違反と見なされ、それを検出するのに十分なメモリ管理を備えたシステムは、発生時にSIGSEGVを送信します。

いくつかのオプションがあります。まず、使用可能なスタックの量を推測せずに、システムに問い合わせます。 POSIXに準拠するものはすべて、上限を示すgetrlimit(2)関数を備えています。 _RLIMIT_STACK_は、必要な特定の制限です。 2つ目は、プログラムが使用しているスタックの量を監視し、それに基づいて自動変数と動的メモリ割り当てを決定することです。私の知る限り、使用されているスタックの量を判別する標準関数はありませんが、valgrindのようなプログラムで分析できます。

11
Blrfl

スタックにたとえば10,000バイトの配列を割り当てると、その配列のサイズは制限されます。 10,000は多くなる可能性がありますが、10,001バイトが必要な場合、プログラムがクラッシュするか、さらに悪化する可能性があります。そのため、この状況では、必要なサイズに適応するものが必要であり、スタックにないものを必要とします。

スタック上の文字列バッファの固定サイズの配列は、スタックにメモリを保持するため問題ではありません。固定サイズのバッファは、発生するのを待っている致命的な問題であるため、問題になります。

ただし、C++を使用していて、たとえばstd :: stringやstd :: vecをスタックで宣言すると、スタックにあるものは実際には固定された小さなサイズになります。実際のデータはヒープに格納されます。 std :: stringインスタンスには100万文字を格納でき、スタックには非常に少量のデータ(通常は実装に応じて8〜24バイト)、ヒープには100万バイトが必要です。

4
gnasher729

まあ1 MBは* nixの良い見積もりです。再帰は、スタック割り当てと組み合わせたスタックオーバーフローの主な原因である可能性があります。ただし、ほとんどの場合、表面的にはスタックに配置するには大きすぎるように見える神オブジェクトは、ヒープ上の内部メモリを管理し、スタックがポップされたときに自動的に破壊される方法としてのみスタックを使用するように設計されています。デストラクタは、内部で管理されている大量のメモリを解放します。 stdコンテナはそのように設計されており、共有/一意のポインタも同様に設計されています。

重要なことは、char [1024 * 1024]のようにスタックにraw memの大きなチャンクを割り当てないことと、ヒープ割り当てをラップするクラスを設計し、デストラクタを自動的に呼び出すためにのみスタックを使用することです。

3
Asterisk