Combien d'utilisation de la pile est trop?


22

Dernièrement, lorsque j'ai écrit du C ou du C ++, je déclarerai toutes mes variables sur la pile simplement parce que c'est une option, contrairement à Java.

Cependant, j'ai entendu dire que c'est une mauvaise idée de déclarer de grandes choses sur la pile.

  1. Pourquoi est-ce exactement le cas? Je pense que le débordement de pile est impliqué, mais je ne sais pas très bien pourquoi cela se produit.
  2. Combien de choses sur la pile, c'est trop?

Je n'essaye pas de mettre des fichiers de 100 Mo sur la pile, juste une douzaine de tableaux de kilo-octets à utiliser comme tampons de chaîne ou autre. Est-ce trop d'utilisation de la pile?

(Désolé en cas de doublon, la recherche de la pile continue de donner des références à Stack Overflow. Il n'y a même pas de balise de pile d'appel, je viens d'utiliser l'abstrait.)


1
Comment "mettez-vous des fichiers de 100 Mo sur la pile"? Les implémentations de tampons et de conteneurs (et similaires comme std :: string) utilisent généralement le tas pour stocker leur charge utile.
Murphy

2
Vous pouvez vous en sortir avec une bonne quantité d'utilisation de la pile par fonction / méthode jusqu'à ce que la récursivité soit impliquée, puis vous risquez de limiter considérablement vos capacités, vis-à-vis de la profondeur récursive, donc dans les fonctions récursives, vous voulez utiliser aussi peu de local espace variable / pile possible.
Erik Eidt

3
Notez que C & C ++ sont différents. Une std::vector<int>variable locale ne consommera pas beaucoup d'espace de pile, la plupart des données étant en tas.
Basile Starynkevitch

Réponses:


18

Cela dépend de votre système d'exploitation. Sous Windows, la taille maximale typique d'une pile est de 1 Mo, alors qu'elle est de 8 Mo sur un Linux moderne typique, bien que ces valeurs soient ajustables de diverses manières. Si la somme de vos variables de pile (y compris les frais généraux de bas niveau tels que les adresses de retour, les arguments basés sur la pile, les espaces réservés de valeur de retour et les octets d'alignement) dans la pile d'appel entière dépasse cette limite, vous obtenez un débordement de pile, qui supprime généralement votre programme sans aucune chance de récupération.

Quelques kilo-octets suffisent généralement. Des dizaines de kilo-octets sont dangereux car ils commencent à résumer. Des centaines de kilo-octets sont une très mauvaise idée.


1
La pile typique ne limite-t-elle pas plusieurs mégaoctets (c'est-à-dire généralement plus d'un, mais probablement moins d'une douzaine) aujourd'hui en 2016? Sur mon bureau Linux, il est de 8 Mo par défaut ...
Basile Starynkevitch

"Sous [...] Linux, la taille maximale type d'une pile est de 1 Mo" $ ulimit -a, entre autres sur mon système stack size (kbytes, -s) 8192.
Murphy

9

La seule réponse valable est vague: "trop, c'est quand la pile déborde."

À moins que vous ne maîtrisiez complètement l'implémentation de chaque ligne de code entre le point d'entrée du programme et la fonction en question, vous ne pouvez faire aucune hypothèse sur la quantité de pile disponible. Vous ne pouvez pas, par exemple, garantir que l'appel de cette fonction ne provoquera jamais un débordement de pile:

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

La pile par défaut de 8 Mio sur les Unix modernes est beaucoup de place pour les piles, en particulier pour quelqu'un comme moi qui est assez bon pour se souvenir des processeurs avec des pointeurs de pile 8 bits. La réalité pratique est qu'il est peu probable que vous passiez à travers sans essayer. Si vous le faites, le dépassement de la limite de pile est généralement considéré comme une violation de segmentation, et les systèmes avec suffisamment de gestion de la mémoire pour le détecter en enverront un SIGSEGVquand cela se produira .

Vous avez quelques options. La première consiste à ne pas deviner la quantité de pile disponible et à demander au système. Tout ce qui est conforme à POSIX aura une getrlimit(2)fonction qui vous indiquera la limite supérieure. RLIMIT_STACKest la limite spécifique que vous souhaitez. La seconde consiste à surveiller la quantité de pile utilisée par vos programmes et à prendre des décisions sur les variables automatiques par rapport à l'allocation dynamique de mémoire en fonction de cela. Il n'y a, pour autant que je sache, aucune fonction standard pour déterminer la quantité de la pile utilisée, mais des programmes comme valgrindpeuvent l'analyser pour vous.


4

Si vous allouez un tableau de 10 000 octets par exemple à la pile, ce tableau est de taille limitée. 10 000 peut être beaucoup, mais si vous avez besoin de 10 001 octets, votre programme peut se bloquer ou pire. Donc, dans cette situation, vous voulez quelque chose qui s'adapte à la taille dont vous avez besoin et que quelque chose ne soit pas sur la pile.

Les tableaux de taille fixe pour les tampons de chaîne sur la pile ne sont pas un problème car ils gardent la mémoire sur la pile, ils sont un problème parce que les tampons de taille fixe sont un problème fatal qui attend de se produire.

Mais si vous utilisez C ++ et déclarez par exemple une chaîne std :: ou std :: vec sur la pile, alors ce qui se trouve sur la pile sera en fait d'une taille fixe et petite. Les données réelles seront stockées sur le tas. Vous pouvez stocker un million de caractères dans une instance std :: string, et cela ne prendra qu'une très petite quantité de données (généralement 8 à 24 octets, selon l'implémentation) sur la pile, et un million d'octets sur le tas.


2

Eh bien 1 Mo est une bonne estimation pour * nix. La récursivité peut être une raison majeure de débordement de pile en combinaison avec des allocations de pile. Cependant, dans la plupart des cas, les objets divins qui sembleraient superficiellement trop gros pour être placés sur la pile sont bien conçus pour gérer leur mémoire interne sur le tas et utiliser la pile uniquement comme un moyen d'être automatiquement détruit lorsque la pile est sautée. Le destructeur libérera les énormes morceaux de mémoire gérés en interne. Les conteneurs std sont conçus de cette façon, et les pointeurs partagés / uniques sont également conçus de cette façon.

L'important est de ne pas allouer de gros morceaux de mem brut sur la pile comme char [1024 * 1024] et de concevoir des classes pour encapsuler les allocations de tas et utiliser la pile uniquement pour la commodité d'appeler automatiquement le destructeur.

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.