Les livres de langage de programmation expliquent que les types de valeur sont créés sur la pile et les types de référence sont créés sur le tas , sans expliquer ce que sont ces deux choses. Je n'ai pas lu d'explication claire à ce sujet. Je comprends ce qu'est une pile . Mais,
- Où et que sont-ils (physiquement dans la mémoire d'un vrai ordinateur)?
- Dans quelle mesure sont-ils contrôlés par le système d'exploitation ou l'exécution du langage?
- Quelle est leur portée?
- Qu'est-ce qui détermine la taille de chacun d'eux?
- Qu'est-ce qui rend un plus rapide?
rlimit_stack
. Voir également le numéro 1463241 deRéponses:
La pile est la mémoire mise de côté comme espace de travail pour un thread d'exécution. Lorsqu'une fonction est appelée, un bloc est réservé en haut de la pile pour les variables locales et certaines données de comptabilité. Lorsque cette fonction revient, le bloc devient inutilisé et peut être utilisé la prochaine fois qu'une fonction est appelée. La pile est toujours réservée dans un ordre LIFO (dernier entré premier sorti); le bloc le plus récemment réservé est toujours le bloc suivant à libérer. Cela rend très simple le suivi de la pile; libérer un bloc de la pile n'est rien d'autre que régler un pointeur.
Le tas est de la mémoire réservée à l'allocation dynamique. Contrairement à la pile, il n'y a pas de modèle appliqué à l'allocation et à la désallocation des blocs du tas; vous pouvez allouer un bloc à tout moment et le libérer à tout moment. Cela rend beaucoup plus complexe le suivi des parties du tas allouées ou libres à un moment donné; de nombreux allocateurs de segments de mémoire personnalisés sont disponibles pour ajuster les performances des segments de mémoire pour différents modèles d'utilisation.
Chaque thread obtient une pile, alors qu'il n'y a généralement qu'un seul segment pour l'application (bien qu'il ne soit pas rare d'avoir plusieurs segments pour différents types d'allocation).
Pour répondre directement à vos questions:
Le système d'exploitation alloue la pile pour chaque thread au niveau du système lorsque le thread est créé. En règle générale, le système d'exploitation est appelé par le langage d'exécution pour allouer le segment de mémoire à l'application.
La pile est attachée à un thread, donc lorsque le thread quitte la pile est récupérée. Le segment de mémoire est généralement alloué au démarrage de l'application par le runtime et est récupéré lorsque l'application (techniquement processus) se ferme.
La taille de la pile est définie lors de la création d'un thread. La taille du segment de mémoire est définie au démarrage de l'application, mais peut augmenter en fonction de l'espace nécessaire (l'allocateur demande plus de mémoire au système d'exploitation).
La pile est plus rapide car le modèle d'accès rend banale l'allocation et la désallocation de mémoire (un pointeur / entier est simplement incrémenté ou décrémenté), tandis que le tas a une comptabilité beaucoup plus complexe impliquée dans une allocation ou une désallocation. De plus, chaque octet de la pile a tendance à être réutilisé très fréquemment, ce qui signifie qu'il a tendance à être mappé au cache du processeur, ce qui le rend très rapide. Un autre impact sur les performances du tas est que le tas, étant principalement une ressource globale, doit généralement être sécurisé pour plusieurs threads, c'est-à-dire que chaque allocation et désallocation doit être - généralement - synchronisée avec "tous" les autres accès de tas du programme.
Une démonstration claire:
Source de l'image: vikashazrati.wordpress.com
la source
Empiler:
Tas:
delete
,delete[]
, oufree
.new
oumalloc
respectivement.Exemple:
la source
C
langage, tel que défini par laC99
norme de langage (disponible sur open-std.org/JTC1/SC22/WG14/www/docs/n1256.pdf ), nécessite une "pile". En fait, le mot «pile» n'apparaît même pas dans la norme. Cela répond que les déclarations surC
l'utilisation de la pile par wrt / to sont vraies en général, mais ne sont en aucun cas requises par le langage. Voir knosof.co.uk/cbook/cbook.html pour plus d'informations, et en particulier commentC
est mis en œuvre sur des architectures impaires telles que en.wikipedia.org/wiki/Burroughs_large_systemsLe point le plus important est que le tas et la pile sont des termes génériques pour les façons dont la mémoire peut être allouée. Ils peuvent être mis en œuvre de différentes manières et les termes s'appliquent aux concepts de base.
Dans une pile d'objets, les objets sont placés les uns sur les autres dans l'ordre où ils ont été placés, et vous ne pouvez retirer que celui du haut (sans renverser le tout).
La simplicité d'une pile est que vous n'avez pas besoin de maintenir une table contenant un enregistrement de chaque section de mémoire allouée; les seules informations d'état dont vous avez besoin sont un seul pointeur vers la fin de la pile. Pour allouer et désallouer, il vous suffit d'incrémenter et de décrémenter ce pointeur unique. Remarque: une pile peut parfois être implémentée pour démarrer en haut d'une section de mémoire et s'étendre vers le bas plutôt que de croître vers le haut.
Dans un tas, il n'y a pas d'ordre particulier à la façon dont les éléments sont placés. Vous pouvez atteindre et supprimer des articles dans n'importe quel ordre, car il n'y a pas d'élément «supérieur» clair.
L'allocation de segments nécessite de conserver un enregistrement complet de la mémoire allouée et de ce qui ne l'est pas, ainsi qu'une maintenance de surcharge pour réduire la fragmentation, trouver des segments de mémoire contigus suffisamment grands pour s'adapter à la taille demandée, etc. La mémoire peut être désallouée à tout moment en laissant de l'espace libre. Parfois, un allocateur de mémoire effectuera des tâches de maintenance telles que la défragmentation de la mémoire en déplaçant la mémoire allouée ou le ramasse-miettes - identifiant au moment de l'exécution lorsque la mémoire n'est plus dans la portée et la désallouant.
Ces images devraient décrire assez bien les deux façons d'allouer et de libérer de la mémoire dans une pile et un tas. Miam!
Dans quelle mesure sont-ils contrôlés par le système d'exploitation ou le langage d'exécution?
Comme mentionné, tas et pile sont des termes généraux et peuvent être implémentés de plusieurs façons. Les programmes informatiques ont généralement une pile appelée pile d' appels qui stocke des informations pertinentes pour la fonction actuelle, comme un pointeur vers la fonction à partir de laquelle elle a été appelée et toutes les variables locales. Étant donné que les fonctions appellent d'autres fonctions puis reviennent, la pile s'agrandit et se rétrécit pour contenir les informations des fonctions situées plus bas dans la pile d'appels. Un programme n'a pas vraiment de contrôle d'exécution sur celui-ci; il est déterminé par le langage de programmation, le système d'exploitation et même l'architecture du système.
Un tas est un terme général utilisé pour toute mémoire allouée de manière dynamique et aléatoire; c'est-à-dire hors service. La mémoire est généralement allouée par le système d'exploitation, l'application appelant des fonctions API pour effectuer cette allocation. La gestion de la mémoire allouée dynamiquement nécessite un peu de surcharge, généralement gérée par le code d'exécution du langage de programmation ou de l'environnement utilisé.
Quelle est leur portée?
La pile d'appels est un concept tellement bas qu'il ne se rapporte pas à la «portée» au sens de la programmation. Si vous démontez du code, vous verrez des références de style de pointeur relatives à des parties de la pile, mais en ce qui concerne un langage de niveau supérieur, le langage impose ses propres règles de portée. Cependant, un aspect important d'une pile est qu'une fois qu'une fonction revient, tout ce qui est local à cette fonction est immédiatement libéré de la pile. Cela fonctionne comme vous vous attendez à ce qu'il fonctionne, compte tenu du fonctionnement de vos langages de programmation. Dans un tas, il est également difficile à définir. La portée est tout ce qui est exposé par le système d'exploitation, mais votre langage de programmation ajoute probablement ses règles sur ce qu'est une «portée» dans votre application. L'architecture du processeur et le système d'exploitation utilisent l'adressage virtuel, que le processeur traduit en adresses physiques et il y a des défauts de page, etc. Ils gardent une trace des pages qui appartiennent à quelles applications. Cependant, vous n'avez jamais vraiment à vous en préoccuper, car vous utilisez simplement la méthode utilisée par votre langage de programmation pour allouer et libérer de la mémoire et vérifier les erreurs (si l'allocation / la libération échoue pour une raison quelconque).
Qu'est-ce qui détermine la taille de chacun d'eux?
Encore une fois, cela dépend du langage, du compilateur, du système d'exploitation et de l'architecture. Une pile est généralement pré-allouée car, par définition, elle doit être une mémoire contiguë. Le compilateur de langue ou le système d'exploitation déterminent sa taille. Vous ne stockez pas d'énormes blocs de données sur la pile, elle sera donc suffisamment grande pour ne jamais être utilisée à fond, sauf en cas de récursion sans fin indésirable (d'où le "débordement de pile") ou d'autres décisions de programmation inhabituelles.
Un tas est un terme général pour tout ce qui peut être alloué dynamiquement. Selon la façon dont vous le regardez, il change constamment de taille. Dans les processeurs et les systèmes d'exploitation modernes, la façon exacte dont il fonctionne est de toute façon très abstraite, vous n'avez donc normalement pas à vous soucier de la façon dont il fonctionne en profondeur, sauf que (dans les langues où cela vous permet), vous ne devez pas utiliser de mémoire qui vous n'avez pas encore alloué ou de mémoire que vous avez libérée.
Qu'est-ce qui rend un plus rapide?
La pile est plus rapide car toute la mémoire libre est toujours contiguë. Aucune liste ne doit être conservée de tous les segments de mémoire libre, juste un seul pointeur vers le haut actuel de la pile. Les compilateurs stockent généralement ce pointeur dans un registre spécial et rapide à cet effet. De plus, les opérations ultérieures sur une pile sont généralement concentrées dans des zones de mémoire très proches, ce qui, à un niveau très bas, est bon pour l'optimisation par les caches en processeur du processeur.
la source
(J'ai déplacé cette réponse d'une autre question qui était plus ou moins une dupe de celle-ci.)
La réponse à votre question est spécifique à l'implémentation et peut varier selon les compilateurs et les architectures de processeur. Cependant, voici une explication simplifiée.
Le tas
new
oumalloc
) sont satisfaites en créant un bloc approprié à partir de l'un des blocs libres. Cela nécessite de mettre à jour la liste des blocs sur le tas. Ces méta-informations sur les blocs sur le tas sont également stockées sur le tas souvent dans une petite zone juste en face de chaque bloc.La pile
Non, les enregistrements d'activation pour les fonctions (c'est-à-dire les variables locales ou automatiques) sont alloués sur la pile qui est utilisée non seulement pour stocker ces variables, mais aussi pour garder une trace des appels de fonction imbriqués.
La façon dont le tas est géré dépend vraiment de l'environnement d'exécution. C utilise
malloc
et utilise C ++new
, mais de nombreux autres langages ont une récupération de place.Cependant, la pile est une fonctionnalité de bas niveau étroitement liée à l'architecture du processeur. Agrandir le tas lorsqu'il n'y a pas assez d'espace n'est pas trop difficile car il peut être implémenté dans l'appel de bibliothèque qui gère le tas. Cependant, l'agrandissement de la pile est souvent impossible car le débordement de pile n'est découvert que lorsqu'il est trop tard; et la fermeture du thread d'exécution est la seule option viable.
la source
Dans le code C # suivant
Voici comment la mémoire est gérée
Local Variables
qui n'ont besoin de durer aussi longtemps que l'invocation de la fonction va dans la pile. Le tas est utilisé pour les variables dont nous ne connaissons pas vraiment la durée de vie mais nous nous attendons à ce qu'elles durent un certain temps. Dans la plupart des langages, il est essentiel de savoir au moment de la compilation la taille d'une variable si nous voulons la stocker sur la pile.Les objets (dont la taille varie au fur et à mesure que nous les mettons à jour) vont sur le tas car nous ne savons pas au moment de la création combien de temps ils vont durer. Dans de nombreuses langues, le tas est récupéré pour trouver des objets (tels que l'objet cls1) qui n'ont plus de références.
En Java, la plupart des objets vont directement dans le tas. Dans des langages comme C / C ++, les structures et les classes peuvent souvent rester sur la pile lorsque vous ne traitez pas avec des pointeurs.
Plus d'informations peuvent être trouvées ici:
La différence entre la pile et l'allocation de mémoire en tas «timmurphy.org
et ici:
Création d'objets sur la pile et le tas
Cet article est la source de l'image ci-dessus: Six concepts .NET importants: pile, segment de mémoire, types de valeur, types de référence, boxing et unboxing - CodeProject
mais sachez qu'il peut contenir des inexactitudes.
la source
La pile Lorsque vous appelez une fonction, les arguments de cette fonction ainsi que d'autres frais généraux sont placés sur la pile. Certaines informations (comme où aller au retour) y sont également stockées. Lorsque vous déclarez une variable à l'intérieur de votre fonction, cette variable est également allouée sur la pile.
La désallocation de la pile est assez simple car vous désallouez toujours dans l'ordre inverse dans lequel vous l'allouez. Stack stuff est ajouté lorsque vous entrez dans les fonctions, les données correspondantes sont supprimées lorsque vous les quittez. Cela signifie que vous avez tendance à rester dans une petite région de la pile, sauf si vous appelez de nombreuses fonctions qui appellent de nombreuses autres fonctions (ou créez une solution récursive).
Le tas Le tas est un nom générique pour l'endroit où vous placez les données que vous créez à la volée. Si vous ne savez pas combien de vaisseaux spatiaux votre programme va créer, vous utiliserez probablement le nouvel opérateur (ou malloc ou équivalent) pour créer chaque vaisseau spatial. Cette allocation va durer un certain temps, il est donc probable que nous libérions les choses dans un ordre différent de celui que nous avons créé.
Ainsi, le tas est beaucoup plus complexe, car il finit par être des régions de mémoire qui sont inutilisées entrelacées avec des morceaux qui sont - la mémoire est fragmentée. Trouver de la mémoire libre de la taille dont vous avez besoin est un problème difficile. C'est pourquoi le tas doit être évité (bien qu'il soit encore souvent utilisé).
Implémentation L' implémentation de la pile et du tas se fait généralement au niveau de l'exécution / du système d'exploitation. Souvent, les jeux et autres applications dont les performances sont essentielles créent leurs propres solutions de mémoire qui récupèrent une grande partie de la mémoire du tas, puis la répartissent en interne pour éviter de dépendre du système d'exploitation pour la mémoire.
Cela n'est pratique que si votre utilisation de la mémoire est assez différente de la norme - c'est-à-dire pour les jeux où vous chargez un niveau en une seule opération énorme et pouvez tout jeter dans une autre opération énorme.
Emplacement physique en mémoire Ceci est moins pertinent que vous ne le pensez en raison d'une technologie appelée mémoire virtuelle qui fait penser à votre programme que vous avez accès à une certaine adresse où les données physiques sont ailleurs (même sur le disque dur!). Les adresses que vous obtenez pour la pile sont en ordre croissant à mesure que votre arbre d'appels s'approfondit. Les adresses du tas sont imprévisibles (c'est-à-dire spécifiques à l'implantation) et franchement pas importantes.
la source
Pour clarifier, cette réponse contient des informations incorrectes ( Thomas a corrigé sa réponse après les commentaires, cool :)). D'autres réponses évitent simplement d'expliquer ce que signifie l'allocation statique. J'expliquerai donc les trois principales formes d'allocation et comment elles sont généralement liées au segment de tas, de pile et de données ci-dessous. Je vais également montrer quelques exemples en C / C ++ et en Python pour aider les gens à comprendre.
Les variables "statiques" (AKA allouées statiquement) ne sont pas allouées sur la pile. Ne le supposez pas - beaucoup de gens le font seulement parce que "statique" ressemble beaucoup à "pile". Ils n'existent en fait ni dans la pile ni dans le tas. Ils font partie de ce qu'on appelle le segment de données .
Cependant, il est généralement préférable de considérer « portée » et « durée de vie » plutôt que «pile» et «tas».
La portée fait référence aux parties du code qui peuvent accéder à une variable. En général, nous pensons à la portée locale (accessible uniquement par la fonction actuelle) par rapport à la portée globale (accessible n'importe où), bien que la portée puisse devenir beaucoup plus complexe.
La durée de vie fait référence à quand une variable est allouée et désallouée pendant l'exécution du programme. Habituellement, nous pensons à l'allocation statique (la variable persistera pendant toute la durée du programme, ce qui la rend utile pour stocker les mêmes informations sur plusieurs appels de fonction) à l'allocation automatique (la variable ne persiste que pendant un seul appel à une fonction, ce qui la rend utile pour stockage des informations qui ne sont utilisées que pendant votre fonction et peuvent être supprimées une fois que vous avez terminé) par rapport à l'allocation dynamique (variables dont la durée est définie au moment de l'exécution, au lieu du temps de compilation comme statique ou automatique).
Bien que la plupart des compilateurs et des interprètes implémentent ce comportement de manière similaire en termes d'utilisation de piles, tas, etc., un compilateur peut parfois rompre ces conventions s'il le souhaite tant que le comportement est correct. Par exemple, en raison de l'optimisation, une variable locale ne peut exister que dans un registre ou être supprimée entièrement, même si la plupart des variables locales existent dans la pile. Comme cela a été souligné dans quelques commentaires, vous êtes libre d'implémenter un compilateur qui n'utilise même pas de pile ou de tas, mais à la place d'autres mécanismes de stockage (rarement effectués, car les piles et les tas sont parfaits pour cela).
Je vais fournir un code C annoté simple pour illustrer tout cela. La meilleure façon d'apprendre est d'exécuter un programme sous un débogueur et de surveiller le comportement. Si vous préférez lire python, passez à la fin de la réponse :)
Un exemple particulièrement poignant de la raison pour laquelle il est important de faire la distinction entre durée de vie et portée est qu'une variable peut avoir une portée locale mais une durée de vie statique - par exemple, "someLocalStaticVariable" dans l'exemple de code ci-dessus. De telles variables peuvent rendre nos habitudes de dénomination communes mais informelles très déroutantes. Par exemple, lorsque nous disons « local », nous voulons généralement dire « variable allouée automatiquement à portée locale » et lorsque nous disons global, nous voulons dire « variable allouée statiquement à portée mondiale ». Malheureusement, quand il s'agit de choses comme " les variables allouées statiquement à portée de fichier ", beaucoup de gens disent juste ... " hein ??? ".
Certains choix de syntaxe en C / C ++ aggravent ce problème - par exemple, beaucoup de gens pensent que les variables globales ne sont pas "statiques" en raison de la syntaxe indiquée ci-dessous.
Notez que la mise du mot clé "statique" dans la déclaration ci-dessus empêche var2 d'avoir une portée globale. Néanmoins, le var1 global a une allocation statique. Ce n'est pas intuitif! Pour cette raison, j'essaie de ne jamais utiliser le mot "statique" lors de la description de la portée, et de dire à la place quelque chose comme "fichier" ou "fichier limité". Cependant, de nombreuses personnes utilisent l'expression «statique» ou «étendue statique» pour décrire une variable qui n'est accessible qu'à partir d'un seul fichier de code. Dans le contexte de la durée de vie, "statique" signifie toujours que la variable est allouée au démarrage du programme et désallouée à la fin du programme.
Certaines personnes pensent que ces concepts sont spécifiques à C / C ++. Ils ne sont pas. Par exemple, l'exemple Python ci-dessous illustre les trois types d'allocation (il y a quelques différences subtiles possibles dans les langages interprétés que je n'entrerai pas ici).
la source
PostScript
ont plusieurs piles, mais ont un "tas" qui se comporte plus comme une pile.D'autres ont assez bien répondu aux grands traits, je vais donc apporter quelques détails.
La pile et le tas n'ont pas besoin d'être singuliers. Une situation courante dans laquelle vous avez plus d'une pile est si vous avez plus d'un thread dans un processus. Dans ce cas, chaque thread a sa propre pile. Vous pouvez également avoir plusieurs segments de mémoire, par exemple, certaines configurations de DLL peuvent entraîner l'allocation de différentes DLL à partir de différents segments de mémoire, c'est pourquoi il est généralement mauvais de libérer de la mémoire allouée par une bibliothèque différente.
En C, vous pouvez bénéficier de l'allocation de longueur variable en utilisant alloca , qui alloue sur la pile, par opposition à alloc, qui alloue sur le tas. Cette mémoire ne survivra pas à votre déclaration de retour, mais elle est utile pour un tampon de travail.
Faire un énorme tampon temporaire sur Windows dont vous n'utilisez pas beaucoup n'est pas gratuit. Cela est dû au fait que le compilateur générera une boucle de sonde de pile qui est appelée à chaque fois que votre fonction est entrée pour vous assurer que la pile existe (car Windows utilise une seule page de garde à la fin de votre pile pour détecter le moment où elle doit agrandir la pile. Si vous accédez à la mémoire de plusieurs pages à la fin de la pile, vous vous planterez). Exemple:
la source
alloca
?D'autres ont répondu directement à votre question, mais en essayant de comprendre la pile et le tas, je pense qu'il est utile de considérer la disposition de la mémoire d'un processus UNIX traditionnel (sans threads et
mmap()
allocateurs basés sur). La page Web Glossaire de gestion de la mémoire contient un schéma de cette disposition de la mémoire.La pile et le tas sont traditionnellement situés aux extrémités opposées de l'espace d'adressage virtuel du processus. La pile se développe automatiquement lors de l'accès, jusqu'à une taille définie par le noyau (qui peut être ajustée avec
setrlimit(RLIMIT_STACK, ...)
). Le segment s'agrandit lorsque l'allocateur de mémoire appelle l' appel systèmebrk()
ousbrk()
, mappant plus de pages de mémoire physique dans l'espace d'adressage virtuel du processus.Dans les systèmes sans mémoire virtuelle, tels que certains systèmes intégrés, la même disposition de base s'applique souvent, sauf que la pile et le tas sont de taille fixe. Cependant, dans d'autres systèmes embarqués (tels que ceux basés sur les microcontrôleurs Microchip PIC), la pile de programmes est un bloc de mémoire distinct qui n'est pas adressable par les instructions de déplacement des données, et ne peut être modifié ou lu qu'indirectement via les instructions de flux de programme (appel, retour, etc.). D'autres architectures, telles que les processeurs Intel Itanium, ont plusieurs piles . En ce sens, la pile est un élément de l'architecture CPU.
la source
Qu'est-ce qu'une pile?
Une pile est une pile d'objets, généralement celle qui est soigneusement organisée.
Qu'est-ce qu'un tas?
Un tas est une collection désordonnée de choses empilées au hasard.
Les deux ensemble
Quel est le plus rapide - la pile ou le tas? Et pourquoi?
Pour les débutants en programmation, c'est probablement une bonne idée d'utiliser la pile car c'est plus facile.
Parce que la pile est petite, vous voudrez l'utiliser lorsque vous savez exactement combien de mémoire vous aurez besoin pour vos données, ou si vous savez que la taille de vos données est très petite.
Il est préférable d'utiliser le tas lorsque vous savez que vous aurez besoin de beaucoup de mémoire pour vos données, ou que vous n'êtes pas sûr de la quantité de mémoire dont vous aurez besoin (comme avec un tableau dynamique).
Modèle de mémoire Java
La pile est la zone de mémoire où sont stockées les variables locales (y compris les paramètres de méthode). En ce qui concerne les variables d'objet, ce ne sont que des références (pointeurs) aux objets réels sur le tas.
Chaque fois qu'un objet est instancié, un morceau de mémoire de tas est mis de côté pour contenir les données (état) de cet objet. Étant donné que les objets peuvent contenir d'autres objets, certaines de ces données peuvent en fait contenir des références à ces objets imbriqués.
la source
La pile est une partie de la mémoire qui peut être manipulée via plusieurs instructions de langage d'assemblage clés, telles que 'pop' (supprimer et renvoyer une valeur de la pile) et 'push' (pousser une valeur dans la pile), mais aussi appeler ( appeler un sous-programme - cela pousse l'adresse à retourner dans la pile) et retourner (retour d'un sous-programme - cela fait sortir l'adresse de la pile et y saute). C'est la région de mémoire sous le registre de pointeur de pile, qui peut être définie selon les besoins. La pile est également utilisée pour transmettre des arguments aux sous-programmes, ainsi que pour conserver les valeurs dans les registres avant d'appeler des sous-programmes.
Le tas est une partie de la mémoire qui est donnée à une application par le système d'exploitation, généralement via un appel système comme malloc. Sur les systèmes d'exploitation modernes, cette mémoire est un ensemble de pages auxquelles seul le processus appelant a accès.
La taille de la pile est déterminée lors de l'exécution et n'augmente généralement pas après le lancement du programme. Dans un programme C, la pile doit être suffisamment grande pour contenir chaque variable déclarée dans chaque fonction. Le tas augmentera dynamiquement selon les besoins, mais le système d'exploitation fait finalement l'appel (il augmentera souvent le tas de plus que la valeur demandée par malloc, de sorte qu'au moins certains futurs mallocs n'auront pas besoin de retourner au noyau pour obtenir plus de mémoire. Ce comportement est souvent personnalisable)
Parce que vous avez alloué la pile avant de lancer le programme, vous n'avez jamais besoin de malloc avant de pouvoir utiliser la pile, c'est donc un léger avantage. En pratique, il est très difficile de prédire ce qui sera rapide et ce qui sera lent dans les systèmes d'exploitation modernes qui ont des sous-systèmes de mémoire virtuelle, car la façon dont les pages sont implémentées et où elles sont stockées est un détail d'implémentation.
la source
Je pense que beaucoup d'autres personnes vous ont donné des réponses plutôt correctes à ce sujet.
Un détail qui a cependant été omis est que le "tas" devrait en fait probablement être appelé le "magasin gratuit". La raison de cette distinction est que le magasin gratuit d'origine a été implémenté avec une structure de données connue sous le nom de "tas binomial". Pour cette raison, l'allocation depuis les premières implémentations de malloc () / free () était l'allocation à partir d'un tas. Cependant, de nos jours, la plupart des magasins gratuits sont mis en œuvre avec des structures de données très élaborées qui ne sont pas des tas binomiaux.
la source
C
langue. Il s'agit d'une idée fausse courante, bien que ce soit le paradigme (de loin) dominant pour la mise en œuvreC99 6.2.4 automatic storage duration objects
(variables). En fait, le mot «pile» n'apparaît même pas dans leC99
langage standard: open-std.org/JTC1/SC22/WG14/www/docs/n1256.pdfVous pouvez faire des choses intéressantes avec la pile. Par exemple, vous avez des fonctions comme alloca (en supposant que vous pouvez passer les avertissements copieux concernant son utilisation), qui est une forme de malloc qui utilise spécifiquement la pile, pas le tas, pour la mémoire.
Cela dit, les erreurs de mémoire basées sur la pile sont parmi les pires que j'ai connues. Si vous utilisez la mémoire de tas et que vous dépassez les limites de votre bloc alloué, vous avez une chance décente de déclencher une erreur de segment. (Pas à 100%: votre bloc peut être accessoirement contigu à un autre que vous avez précédemment alloué.) Mais comme les variables créées sur la pile sont toujours contiguës, l'écriture hors limites peut modifier la valeur d'une autre variable. J'ai appris que chaque fois que je sens que mon programme a cessé d'obéir aux lois de la logique, c'est probablement un débordement de tampon.
la source
alloca
? Par exemple, cela fonctionne-t-il sous Windows? Est-ce uniquement pour les systèmes d'exploitation de type Unix?Simplement, la pile est l'endroit où les variables locales sont créées. En outre, chaque fois que vous appelez un sous-programme, le compteur de programme (pointeur vers la prochaine instruction machine) et tous les registres importants, et parfois les paramètres sont poussés sur la pile. Ensuite, toutes les variables locales à l'intérieur du sous-programme sont poussées sur la pile (et utilisées à partir de là). Lorsque le sous-programme se termine, tout cela ressort de la pile. Le PC et les données de registre sont récupérés et remis là où ils se trouvaient, afin que votre programme puisse continuer son chemin joyeux.
Le tas est la zone d'allocations de mémoire dynamique de la mémoire à partir desquelles (appels "nouveaux" ou "allocations" explicites). Il s'agit d'une structure de données spéciale qui peut garder une trace des blocs de mémoire de tailles variables et de leur état d'allocation.
Dans les systèmes "classiques", la RAM était disposée de telle sorte que le pointeur de pile commençait au bas de la mémoire, le pointeur de tas commençait au sommet et ils se développaient l'un vers l'autre. S'ils se chevauchent, vous n'avez plus de RAM. Cela ne fonctionne pas avec les systèmes d'exploitation multi-threads modernes. Chaque thread doit avoir sa propre pile, et ceux-ci peuvent être créés dynamiquement.
la source
De WikiAnwser.
Empiler
Lorsqu'une fonction ou une méthode appelle une autre fonction qui à son tour appelle une autre fonction, etc., l'exécution de toutes ces fonctions reste suspendue jusqu'à ce que la toute dernière fonction renvoie sa valeur.
Cette chaîne d'appels de fonction suspendus est la pile, car les éléments de la pile (appels de fonction) dépendent les uns des autres.
La pile est importante à considérer dans la gestion des exceptions et les exécutions de threads.
Tas
Le tas est simplement la mémoire utilisée par les programmes pour stocker les variables. Les éléments du tas (variables) n'ont aucune dépendance les uns avec les autres et peuvent toujours être consultés de manière aléatoire à tout moment.
la source
Empiler
Tas
la source
OK, simplement et en bref, ils signifient ordonné et non ordonné ...!
Stack : Dans les éléments de la pile, les choses se superposent, ce qui signifie que le traitement sera plus rapide et plus efficace! ...
Il y a donc toujours un index pour pointer l'élément spécifique, le traitement sera également plus rapide, il existe également une relation entre les éléments! ...
Tas : Aucun ordre, le traitement sera plus lent et les valeurs sont gâchées sans ordre ni index spécifique ... il y a des aléas et il n'y a pas de relation entre eux ... donc le temps d'exécution et d'utilisation peut varier ...
Je crée également l'image ci-dessous pour montrer à quoi ils peuvent ressembler:
la source
En bref
Une pile est utilisée pour l'allocation de mémoire statique et un tas pour l'allocation de mémoire dynamique, tous deux stockés dans la RAM de l'ordinateur.
En détail
La pile
La pile est une structure de données "LIFO" (dernier entré, premier sorti), qui est gérée et optimisée par le CPU de manière assez étroite. Chaque fois qu'une fonction déclare une nouvelle variable, elle est "poussée" sur la pile. Ensuite, chaque fois qu'une fonction se termine, toutes les variables poussées sur la pile par cette fonction sont libérées (c'est-à-dire qu'elles sont supprimées). Une fois qu'une variable de pile est libérée, cette région de mémoire devient disponible pour d'autres variables de pile.
L'avantage d'utiliser la pile pour stocker des variables est que la mémoire est gérée pour vous. Vous n'avez pas à allouer de la mémoire à la main, ni à la libérer une fois que vous n'en avez plus besoin. De plus, parce que le CPU organise la mémoire de pile de manière si efficace, la lecture et l'écriture dans les variables de pile sont très rapides.
Plus d'informations peuvent être trouvées ici .
Le tas
Le tas est une région de la mémoire de votre ordinateur qui n'est pas gérée automatiquement pour vous et n'est pas aussi étroitement gérée par le CPU. Il s'agit d'une région de mémoire plus flottante (et plus grande). Pour allouer de la mémoire sur le tas, vous devez utiliser malloc () ou calloc (), qui sont des fonctions C intégrées. Une fois que vous avez alloué de la mémoire sur le tas, vous êtes responsable de l'utilisation de free () pour désallouer cette mémoire une fois que vous n'en avez plus besoin.
Si vous ne le faites pas, votre programme aura ce que l'on appelle une fuite de mémoire. Autrement dit, la mémoire sur le tas sera toujours mise de côté (et ne sera pas disponible pour d'autres processus). Comme nous le verrons dans la section de débogage, il existe un outil appelé Valgrind qui peut vous aider à détecter les fuites de mémoire.
Contrairement à la pile, le tas n'a pas de restrictions de taille sur la taille variable (à part les limitations physiques évidentes de votre ordinateur). La mémoire du tas est légèrement plus lente à lire et à écrire, car il faut utiliser des pointeurs pour accéder à la mémoire sur le tas. Nous parlerons des pointeurs sous peu.
Contrairement à la pile, les variables créées sur le tas sont accessibles par n'importe quelle fonction, n'importe où dans votre programme. Les variables de tas ont une portée essentiellement globale.
Plus d'informations peuvent être trouvées ici .
Les variables allouées sur la pile sont stockées directement dans la mémoire et l'accès à cette mémoire est très rapide, et son allocation est traitée lors de la compilation du programme. Lorsqu'une fonction ou une méthode appelle une autre fonction qui à son tour appelle une autre fonction, etc., l'exécution de toutes ces fonctions reste suspendue jusqu'à ce que la toute dernière fonction renvoie sa valeur. La pile est toujours réservée dans un ordre LIFO, le dernier bloc réservé est toujours le prochain bloc à libérer. Cela rend vraiment simple de garder une trace de la pile, libérer un bloc de la pile n'est rien de plus que d'ajuster un pointeur.
Les variables allouées sur le tas ont leur mémoire allouée au moment de l'exécution et l'accès à cette mémoire est un peu plus lent, mais la taille du tas n'est limitée que par la taille de la mémoire virtuelle. Les éléments du tas n'ont aucune dépendance les uns avec les autres et peuvent toujours être consultés au hasard à tout moment. Vous pouvez allouer un bloc à tout moment et le libérer à tout moment. Cela rend beaucoup plus complexe le suivi des parties du tas allouées ou libres à un moment donné.
Vous pouvez utiliser la pile si vous savez exactement combien de données vous devez allouer avant la compilation et qu'elle n'est pas trop volumineuse. Vous pouvez utiliser le tas si vous ne savez pas exactement combien de données vous aurez besoin lors de l'exécution ou si vous avez besoin d'allouer beaucoup de données.
Dans une situation multithread, chaque thread aura sa propre pile complètement indépendante, mais ils partageront le tas. La pile est spécifique au thread et le tas est spécifique à l'application. La pile est importante à considérer dans la gestion des exceptions et les exécutions de threads.
Chaque thread obtient une pile, alors qu'il n'y a généralement qu'un seul segment pour l'application (bien qu'il ne soit pas rare d'avoir plusieurs segments pour différents types d'allocation).
Au moment de l'exécution, si l'application a besoin de plus de segment, elle peut allouer de la mémoire à partir de la mémoire libre et si la pile a besoin de mémoire, elle peut allouer de la mémoire à partir de la mémoire allouée à la mémoire libre pour l'application.
Même, plus de détails sont donnés ici et ici .
Venons-en maintenant aux réponses à votre question .
Le système d'exploitation alloue la pile pour chaque thread au niveau du système lorsque le thread est créé. En règle générale, le système d'exploitation est appelé par le langage d'exécution pour allouer le segment de mémoire à l'application.
Plus d'informations peuvent être trouvées ici .
Déjà donné en haut.
Plus d'informations peuvent être trouvées ici .
La taille de la pile est définie par le système d' exploitation lors de la création d'un thread. La taille du segment de mémoire est définie au démarrage de l'application, mais elle peut augmenter à mesure que l'espace est nécessaire (l'allocateur demande plus de mémoire au système d'exploitation).
L'allocation de pile est beaucoup plus rapide car elle ne fait que déplacer le pointeur de pile. En utilisant des pools de mémoire, vous pouvez obtenir des performances comparables grâce à l'allocation de tas, mais cela s'accompagne d'une légère complexité supplémentaire et de ses propres maux de tête.
En outre, la pile par rapport au tas n'est pas seulement une considération de performance; il vous en dit également beaucoup sur la durée de vie attendue des objets.
Les détails peuvent être trouvés ici .
la source
Dans les années 1980, UNIX s'est propagé comme des lapins avec de grandes entreprises qui roulaient les leurs. Exxon en avait un, tout comme des dizaines de marques perdues dans l'histoire. La façon dont la mémoire a été organisée était à la discrétion des nombreux implémenteurs.
Un programme C typique a été présenté à plat en mémoire avec une possibilité d'augmenter en changeant la valeur brk (). En règle générale, le HEAP était juste en dessous de cette valeur brk et l'augmentation de brk augmentait la quantité de tas disponible.
La pile unique était typiquement une zone en dessous de HEAP qui était une étendue de mémoire ne contenant rien de valeur jusqu'au sommet du bloc de mémoire fixe suivant. Ce bloc suivant était souvent CODE qui pouvait être écrasé par des données de pile dans l'un des célèbres hacks de son époque.
Un bloc de mémoire typique était BSS (un bloc de valeurs nulles) qui n'a pas été accidentellement mis à zéro dans l'offre d'un fabricant. Un autre était DATA contenant des valeurs initialisées, y compris des chaînes et des nombres. Un troisième était CODE contenant CRT (runtime C), main, fonctions et bibliothèques.
L'avènement de la mémoire virtuelle sous UNIX modifie de nombreuses contraintes. Il n'y a aucune raison objective pour laquelle ces blocs doivent être contigus, ou de taille fixe, ou commandés d'une manière particulière maintenant. Bien sûr, avant UNIX, il y avait Multics qui ne souffrait pas de ces contraintes. Voici un schéma montrant l'une des dispositions de mémoire de cette époque.
la source
pile , tas et données de chaque processus en mémoire virtuelle:
la source
Quelques cents: Je pense que ce sera bien de dessiner une mémoire graphique et plus simple:
Flèches - montrent où se développent la pile et le tas, la taille de la pile de processus a une limite, définie dans le système d'exploitation, les limites de taille de la pile de threads par les paramètres de l'API de création de threads généralement. Heap limitant généralement la taille maximale de la mémoire virtuelle par processus, pour 32 bits 2-4 Go par exemple.
Façon si simple: le tas de processus est général pour le processus et tous les threads à l'intérieur, utilisant pour l'allocation de mémoire dans le cas commun avec quelque chose comme malloc () .
La pile est une mémoire rapide pour le stockage dans des cas de retour de fonction et des variables de cas courants, traités comme des paramètres dans l'appel de fonction, des variables de fonction locales.
la source
Étant donné que certaines réponses sont allées de pair, je vais contribuer mon acarien.
Étonnamment, personne n'a mentionné que plusieurs piles d'appels (c'est-à-dire non liées au nombre de threads au niveau du système d'exploitation) se trouvent non seulement dans les langages exotiques (PostScript) ou les plates-formes (Intel Itanium), mais aussi dans les fibres , les threads verts et quelques implémentations de coroutines .
Les fibres, les fils verts et les coroutines sont à bien des égards similaires, ce qui crée beaucoup de confusion. La différence entre les fibres et les fils verts est que les premiers utilisent le multitâche coopératif, tandis que les seconds peuvent être coopératifs ou préemptifs (ou même les deux). Pour la distinction entre fibres et coroutines, voir ici .
Dans tous les cas, le but des fibres, des fils verts et des coroutines est d'avoir plusieurs fonctions s'exécutant simultanément, mais pas en parallèle (voir cette question SO pour la distinction) au sein d'un seul thread de niveau OS, transférant le contrôle d'avant en arrière les uns des autres de façon organisée.
Lorsque vous utilisez des fibres, des fils verts ou des coroutines, vous disposez généralement d'une pile distincte par fonction. (Techniquement, non seulement une pile, mais tout un contexte d'exécution est par fonction. Plus important encore, les registres du processeur.) Pour chaque thread, il y a autant de piles que de fonctions exécutées simultanément, et le thread bascule entre l'exécution de chaque fonction selon la logique de votre programme. Lorsqu'une fonction se termine, sa pile est détruite. Ainsi, le nombre et la durée de vie des piles sont dynamiques et ne sont pas déterminés par le nombre de threads au niveau du système d'exploitation!
Notez que j'ai dit " ont généralement une pile séparée par fonction". Il existe des implémentations à la fois empilées et sans pile de couroutines. La plupart des notables implémentations C ++ stackful sont Boost.Coroutine et Microsoft PPL s »
async/await
. (Cependant, les fonctions réactivables de C ++ (alias "async
etawait
"), qui ont été proposées en C ++ 17, sont susceptibles d'utiliser des coroutines sans pile.)Une proposition de fibres à la bibliothèque standard C ++ est à venir. En outre, il existe des bibliothèques tierces . Les fils verts sont extrêmement populaires dans des langages comme Python et Ruby.
la source
J'ai quelque chose à partager, même si les principaux points sont déjà couverts.
Empiler
Tas
Note intéressante:
la source
Hou la la! Tant de réponses et je ne pense pas que l'une d'elles ait bien compris ...
1) Où et quoi sont-ils (physiquement dans la mémoire d'un vrai ordinateur)?
La pile est une mémoire qui commence comme l'adresse mémoire la plus élevée allouée à l'image de votre programme, puis sa valeur diminue à partir de là. Il est réservé aux paramètres de fonction appelés et à toutes les variables temporaires utilisées dans les fonctions.
Il y a deux tas: public et privé.
Le segment privé commence sur une limite de 16 octets (pour les programmes 64 bits) ou sur une limite de 8 octets (pour les programmes 32 bits) après le dernier octet de code de votre programme, puis augmente en valeur à partir de là. Il est également appelé tas par défaut.
Si le tas privé devient trop grand, il chevauchera la zone de pile, tout comme la pile chevauchera le tas s'il devient trop grand. Étant donné que la pile commence à une adresse supérieure et descend jusqu'à une adresse inférieure, avec un piratage approprié, vous pouvez obtenir une pile si grande qu'elle dépassera la zone de tas privée et chevauchera la zone de code. L'astuce consiste alors à chevaucher suffisamment la zone de code que vous pouvez connecter au code. C'est un peu délicat à faire et vous risquez un plantage du programme, mais c'est facile et très efficace.
Le tas public réside dans son propre espace mémoire en dehors de l'espace image de votre programme. C'est cette mémoire qui sera siphonnée sur le disque dur si les ressources mémoire se raréfient.
2) Dans quelle mesure sont-ils contrôlés par le système d'exploitation ou le langage d'exécution?
La pile est contrôlée par le programmeur, le tas privé est géré par le système d'exploitation et le tas public n'est contrôlé par personne car il s'agit d'un service du système d'exploitation - vous faites des demandes et elles sont accordées ou refusées.
2b) Quelle est leur portée?
Ils sont tous globaux pour le programme, mais leur contenu peut être privé, public ou global.
2c) Qu'est-ce qui détermine la taille de chacun d'eux?
La taille de la pile et du segment privé est déterminée par vos options d'exécution du compilateur. Le tas public est initialisé lors de l'exécution à l'aide d'un paramètre de taille.
2d) Qu'est-ce qui rend un plus rapide?
Ils ne sont pas conçus pour être rapides, ils sont conçus pour être utiles. La façon dont le programmeur les utilise détermine si elles sont "rapides" ou "lentes"
RÉF:
https://norasandler.com/2019/02/18/Write-a-Compiler-10.html
https://docs.microsoft.com/en-us/windows/desktop/api/heapapi/nf-heapapi-getprocessheap
https://docs.microsoft.com/en-us/windows/desktop/api/heapapi/nf-heapapi-heapcreate
la source
Beaucoup de réponses sont correctes en tant que concepts, mais il faut noter qu'une pile est nécessaire au matériel (ie microprocesseur) pour permettre d'appeler des sous-routines (CALL en langage assembleur ..). (Les gars OOP l'appelleront méthodes )
Sur la pile, vous enregistrez les adresses de retour et appelez → push / ret → pop est géré directement dans le matériel.
Vous pouvez utiliser la pile pour passer des paramètres .. même si c'est plus lent que d'utiliser des registres (dirait un gourou du microprocesseur ou un bon livre du BIOS des années 80 ...)
L'utilisation de la pile est plus rapide car:
la source
malloc
c'est un appel du noyau?Source: Academind
la source
Merci pour une très bonne discussion mais en tant que vrai noob je me demande où sont conservées les instructions? Au DÉBUT, les scientifiques décidaient entre deux architectures (von NEUMANN où tout est considéré comme DATA et HARVARD où une zone de mémoire était réservée aux instructions et une autre aux données). En fin de compte, nous avons opté pour le design von Neumann et maintenant tout est considéré comme «le même». Cela a été difficile pour moi lorsque j'apprenais l'assemblage https://www.cs.virginia.edu/~evans/cs216/guides/x86.html car ils parlent de registres et de pointeurs de pile.
Tout ce qui précède parle de DATA. Ma conjecture est que, comme une instruction est une chose définie avec une empreinte mémoire spécifique, elle irait sur la pile et donc tous les «ces» registres discutés en assembleur sont sur la pile. Bien sûr, est ensuite venue la programmation orientée objet avec des instructions et des données intégrées dans une structure qui était dynamique, alors maintenant les instructions seraient également conservées sur le tas?
la source