Quanto uso dello stack è troppo?


22

Ultimamente quando scrivo C o C ++, dichiarerò tutte le mie variabili nello stack solo perché è un'opzione, a differenza di Java.

Tuttavia, ho sentito che è una cattiva idea dichiarare grandi cose in pila.

  1. Perché è esattamente così? Immagino che sia coinvolto lo stack overflow, ma non sono molto chiaro sul perché ciò accada.
  2. Quanta roba nello stack è troppo?

Non sto cercando di mettere file da 100 MB nello stack, solo una dozzina di array di kilobyte da usare come buffer di stringa o altro. Questo è un uso eccessivo dello stack?

(Scusate se duplicato, la ricerca dello stack continuava a dare riferimenti a Stack Overflow. Non c'è nemmeno un tag di stack di chiamate, ho appena usato quello astratto.)


1
Come si "mettono in pila file da 100 MB"? Le implementazioni di buffer e container (e simili come std :: string) di solito usano l'heap per memorizzare il loro payload.
Murphy,

2
Puoi cavartela con una buona quantità di utilizzo dello stack per funzione / metodo fino a quando non viene coinvolta la ricorsione, quindi stai rischiando di limitare fortemente le tue capacità, rispetto alla profondità ricorsiva, quindi all'interno delle funzioni ricorsive, vuoi usare un piccolo locale spazio variabile / stack possibile.
Erik Eidt,

3
Si noti che C e C ++ sono diversi. Una std::vector<int>variabile locale non consumerà molto spazio nello stack, la maggior parte dei dati è nell'heap.
Basile Starynkevitch,

Risposte:


18

Dipende dal tuo sistema operativo. Su Windows, la dimensione massima tipica per uno stack è 1 MB, mentre è 8 MB su un tipico Linux moderno, sebbene tali valori siano regolabili in vari modi. Se la somma delle variabili dello stack (incluso l'overhead di basso livello come indirizzi di ritorno, argomenti basati sullo stack, segnaposto del valore di ritorno e byte di allineamento) nell'intero stack di chiamate supera tale limite, si ottiene un overflow dello stack, che generalmente riduce programma senza alcuna possibilità di recupero.

Alcuni kilobyte di solito vanno bene. Decine di kilobyte sono pericolosi perché iniziano a riassumere. Centinaia di kilobyte sono una pessima idea.


1
Lo stack tipico non limita diversi megabyte (cioè di solito più di uno, ma probabilmente meno di una dozzina) oggi nel 2016? Sul mio desktop Linux, per impostazione predefinita sono 8 MByte ...
Basile Starynkevitch

"Su [...] Linux, la dimensione massima tipica per uno stack è di 1 MB" $ ulimit -asul mio sistema ritorna tra gli altri stack size (kbytes, -s) 8192.
Murphy,

9

L'unica risposta valida è vaga: "troppo è quando lo stack trabocca".

A meno che tu non abbia il controllo completo sull'implementazione di ogni riga di codice tra il punto di ingresso del programma e la funzione in questione, non puoi fare ipotesi sulla quantità di stack disponibile. Ad esempio, non è possibile garantire che la chiamata a questa funzione non provochi mai un overflow dello stack:

void break_the_camels_back()
{
    int straw;
    ...
}

Lo stack predefinito di 8 MiB sui moderni Unix è abbastanza spazio per gli stack, specialmente per uno come me che è abbastanza bravo da ricordare CPU con puntatori di stack a 8 bit. La realtà pratica è che è improbabile che ci riesca senza provarci. In tal caso, il superamento del limite dello stack viene generalmente considerato una violazione della segmentazione e i sistemi con una gestione della memoria sufficiente per rilevarlo invieranno un messaggio SIGSEGVquando si verifica.

Hai un paio di opzioni. Il primo è di non indovinare quanto stack è disponibile e chiedere al sistema. Qualunque cosa conforme a POSIX avrà una getrlimit(2)funzione che ti dirà il limite superiore. RLIMIT_STACKè il limite specifico che desideri. Il secondo è monitorare la quantità di stack utilizzata dai programmi e prendere decisioni sulle variabili automatiche rispetto all'allocazione dinamica della memoria in base a ciò. Per quanto ne so, non esistono funzioni standard per determinare la quantità di stack utilizzata, ma programmi come questo valgrindpossono analizzarla per te.


4

Se si alloca un array di circa 10.000 byte nello stack, tale array ha dimensioni limitate. 10.000 possono essere molti, ma se hai bisogno di 10.000 byte, il tuo programma potrebbe bloccarsi o peggio. Quindi, in questa situazione, vuoi qualcosa che si adatti alle dimensioni di cui hai bisogno e che qualcosa non sia nello stack.

Le matrici a dimensione fissa per i buffer di stringa nello stack non sono un problema perché mantengono la memoria nello stack, sono un problema perché i buffer a dimensione fissa sono un problema fatale in attesa di accadere.

Ma se usi C ++ e dichiari ad esempio uno std :: string o uno std :: vec nello stack, ciò che è nello stack avrà effettivamente dimensioni fisse e piccole. I dati effettivi verranno archiviati nell'heap. È possibile memorizzare un milione di caratteri in un'istanza std :: string e saranno necessari solo una quantità molto piccola di dati (in genere da 8 a 24 byte, a seconda dell'implementazione) nello stack e un milione di byte nell'heap.


2

Bene 1 MB è una buona stima per * nix. La ricorsione può essere una delle ragioni principali dell'overflow dello stack in combinazione con le allocazioni dello stack. Tuttavia, nella maggior parte dei casi gli oggetti divini che sembrano superficialmente troppo grandi per essere posizionati in pila sono progettati bene per gestire la loro memoria interna sull'heap e usare lo stack solo come un modo per essere automaticamente distrutti quando lo stack viene espulso. Il distruttore libererà gli enormi blocchi di memoria gestiti internamente. I contenitori std sono progettati in questo modo e anche i puntatori condivisi / unici sono progettati in questo modo.

L'importante è non allocare grandi blocchi di mem grezzi nello stack come char [1024 * 1024] e progettare classi per avvolgere allocazioni di heap e usare lo stack solo per la comodità di chiamare automaticamente il distruttore.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.