Quoi et où sont de la pile et le tas?
langage de Programmation livres expliquer que les types de valeur sont créés sur le pile , et les types de référence sont créés sur le en tas", 151920920" , sans expliquer ce que ces deux choses sont. Je n'ai pas lu une explication claire de ce. 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 sont contrôlés par L'OS ou le language runtime?
- Quelle est leur portée?
- Ce qui détermine la taille de chacun d'eux?
- Qu'est-ce qui rend plus rapide?
25 réponses
la pile est la mémoire mise de côté comme espace de rayure pour un fil 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 comptables. Lorsque cette fonction retourne, 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 (last in first out); le bloc le plus récemment réservé est toujours le bloc suivant à libérer. Cela rend très simple à suivre libérer un bloc de la pile n'est rien d'autre qu'ajuster un pointeur.
le tas est la mémoire mise de côté pour l'allocation dynamique. Contrairement à la pile, il n'y a pas de pattern imposé à l'allocation et à la désallocation des blocs du tas; vous pouvez allouer un bloc à tout moment et le libérer à tout moment. Il est donc beaucoup plus complexe de savoir quelles parties du tas sont allouées ou gratuites à un moment donné; il existe de nombreux allocateurs de tas personnalisés. disponible pour accorder la performance de tas pour différents modèles d'utilisation.
chaque thread reçoit une pile, alors qu'il n'y a généralement qu'un tas pour l'application (bien qu'il ne soit pas rare d'avoir plusieurs tas pour différents types d'allocation).
pour répondre directement à vos questions:
dans quelle mesure sont-ils contrôlés par L'os ou le language runtime?
L'OS affecte la pile pour chaque thread au niveau du système lorsque le thread est créé. Typiquement, L'OS est appelé par la langue runtime pour allouer le tas pour l'application.
Quelle est leur portée?
la pile est attachée à un fil, de sorte que lorsque le fil sort, la pile est récupérée. Le tas est généralement attribué au démarrage de l'application par l'exécution, et est récupéré lorsque la demande (processus technique) est retirée.
Ce qui détermine la taille de chacun d'eux?
la taille de la pile est définie lorsqu'un thread est créé. La taille du tas est définie au démarrage de l'application, mais peut augmenter en fonction de l'espace requis (l'allocateur demande plus de mémoire à partir du système d'exploitation).
Ce qui est plus rapide?
la pile est plus rapide parce que le modèle d'accès le rend trivial pour allouer et désallouer la mémoire à partir de lui (un pointeur/entier est simplement incrémenté ou décrémenté), tandis que le tas a la tenue de livres beaucoup plus complexe impliqué dans une allocation ou 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é dans le cache du processeur, ce qui le rend très rapide. Un autre succès pour the heap est que le tas, étant principalement une ressource globale, doit typiquement être multi-threading sûr, c. - à - d. chaque allocation et deallocation doit être-typiquement-synchronisé avec "tous les autres" accès tas dans le programme.
une démonstration claire:
Image source: vikashazrati.wordpress.com
"151970920 Pile":
- stocké dans la mémoire vive comme le tas. Les Variables
- créées sur la pile sortiront de la portée et seront automatiquement désallouées.
- beaucoup plus rapide à allouer en comparaison aux variables sur le tas.
- implémenté avec une structure réelle de données de cheminée.
- stocke les données locales, les adresses de retour, utilisées pour passage de paramètres.
- peut avoir un débordement de pile quand Trop de la pile est utilisée (principalement à partir d'une récursion infinie ou trop profonde, des allocations très importantes).
- les données créées sur la pile peuvent être utilisées sans pointeurs.
- vous utiliserez la pile si vous savez exactement combien de données vous devez allouer avant le temps de compilation et qu'elle n'est pas trop grande.
- a généralement une taille maximale déjà déterminée lorsque votre programme démarre.
tas:
- stocké en mémoire vive comme la pile.
- En C++, les variables sur le tas doit être détruit manuellement et ne jamais tomber hors de portée. Les données sont libérées par
delete
,delete[]
, oufree
. - plus lent à allouer en comparaison aux variables sur la pile.
- Utilisé sur demande allouer un bloc de données utilisées par le programme.
- peut être fragmenté lorsqu'il y a beaucoup d'allocations et de délocalisations.
- en C++ ou C, les données créées sur le tas seront pointées par des pointeurs et affectées avec
new
oumalloc
respectivement. - peut avoir des échecs d'allocation si trop grand d'un tampon est demandé pour être alloué.
- vous utiliseriez le tas si vous ne savez pas exactement combien de données vous aurez besoin au moment de l'exécution ou si vous avez besoin d'allouer une grande quantité de données.
- Responsable de fuites de mémoire.
exemple:
int foo()
{
char *pBuffer; //<--nothing allocated yet (excluding the pointer itself, which is allocated here on the stack).
bool b = true; // Allocated on the stack.
if(b)
{
//Create 500 bytes on the stack
char buffer[500];
//Create 500 bytes on the heap
pBuffer = new char[500];
}//<-- buffer is deallocated here, pBuffer is not
}//<--- oops there's a memory leak, I should have called delete[] pBuffer;
le 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 attribuée. Ils peuvent être mis en œuvre de nombreuses façons différentes, et les Termes s'appliquent aux concepts de base.
-
dans une pile d'articles, les articles sont assis l'un sur l'autre dans l'ordre où ils ont été placés là, et vous ne pouvez enlever le dessus (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; la seule information d'État dont vous avez besoin est un pointeur simple à la fin de la pile. Pour allouer et dé-allouer, vous juste incrémenter et décrémenter ce pointeur simple. Note: une pile peut parfois être implémentée pour commencer au sommet d'une section de mémoire et s'étendre vers le bas plutôt que vers le haut.
-
dans un tas, il n'y a pas d'ordre particulier à la façon dont les articles sont placés. Vous pouvez atteindre et supprimer des éléments dans n'importe quel ordre parce qu'il n'y a pas d'élément clair "haut".
allocation tas exige de maintenir un enregistrement complet de ce que la mémoire est attribuée et ce qui ne l'est pas, ainsi que certains frais généraux d'entretien pour réduire la fragmentation, trouver des segments de mémoire contigus assez grands pour correspondre à la taille demandée, et ainsi de suite. La mémoire peut être libéré à tout moment en laissant libre l'espace. Parfois, un allocateur de mémoire va effectuer des tâches de maintenance telles que la défragmentation de la mémoire en déplaçant la mémoire allouée autour, ou la collecte des déchets - identifier à l'exécution lorsque la mémoire n'est plus en portée et la désallouer.
ces images devraient faire un assez bon travail de description des deux façons d'allouer et de libérer 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 runtime?
comme nous l'avons mentionné, tas et pile sont des termes généraux qui peuvent être mis en œuvre de nombreuses façons. Les programmes informatiques ont généralement une pile appelée "call stack qui stocke des informations pertinentes à la fonction courante comme un pointeur vers la fonction d'où elle a été appelée, et toute variable locale. Parce que les fonctions appellent d'autres fonctions puis reviennent, la pile croît et se rétrécit pour contenir des informations à partir des fonctions plus loin dans la pile d'appels. Un programme n'a pas vraiment le contrôle de son exécution; il est déterminé par le langage de programmation, L'OS et même l'architecture du système.
un tas est un terme général utilisé pour désigner toute mémoire qui est attribuée de façon dynamique et aléatoire, c'est-à-dire hors de l'ordre. La mémoire est généralement attribuée par le système D'exploitation, l'application appelant des fonctions API pour effectuer cette attribution. Il est un peu juste de surcharge requis dans la gestion de la mémoire allouée dynamiquement, qui est habituellement gérée par L'OS.
-
Quelle est leur portée?
la pile d'appels est un concept de si bas niveau qu'il n'est pas lié à la "portée" au sens de la programmation. Si vous désassemblez un code, vous verrez des références de style pointeur relatives à des portions de la pile, mais en ce qui concerne un niveau de langue plus élevé, la langue impose ses propres règles de portée. Un un aspect important d'une pile, cependant, est qu'une fois qu'une fonction retourne, tout ce qui est local à cette fonction est immédiatement libéré de la pile. Cela fonctionne de la façon dont vous vous attendez à ce que cela fonctionne compte tenu de la façon dont vos langages de programmation fonctionnent. Dans un tas, c'est aussi difficile à définir. Le scope est ce qui est exposé par L'OS, mais votre langage de programmation ajoute probablement ses règles sur ce qu'est un "scope" dans votre application. L'architecture du processeur et L'OS 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 de ce que les pages appartiennent à quelles applications. Vous n'avez jamais vraiment besoin de vous en inquiéter, bien que, parce que vous utilisez juste n'importe quelle méthode que votre langage de programmation utilise pour allouer et libérer la mémoire, et vérifier les erreurs (si l'allocation/libération échoue pour n'importe quelle raison).
-
Qu'est-ce qui détermine la taille de chacun d'eux?
Encore une fois, cela dépend de la langage, compilateur, système d'exploitation et architecture. Une pile est généralement pré-attribuée, parce que par définition elle doit être mémoire contiguë (plus sur celle dans le dernier paragraphe). Le compilateur de langue ou L'OS détermine sa taille. Vous ne stockez pas d'énormes morceaux de données sur la pile, de sorte qu'elle sera assez grande pour ne jamais être pleinement utilisée, sauf en cas de récursion sans fin non désirée (d'où "débordement de la pile") ou d'autres décisions de programmation inhabituelles.
un tas est un terme général désignant tout ce qui peut être affecté dynamiquement. Selon la façon dont vous le regardez, il change constamment de taille. Dans les processeurs modernes et les systèmes d'exploitation, la façon exacte dont cela fonctionne est de toute façon très abstraite, de sorte que vous n'avez normalement pas besoin de vous inquiéter beaucoup de la façon dont cela fonctionne au fond, sauf que (dans les langues où cela vous permet) vous ne devez pas utiliser de mémoire que vous n'avez pas encore attribuée ou de mémoire que vous avez libérée.
-
, Ce qui fait un plus vite?
la pile est plus rapide car toute mémoire libre est toujours contiguë. Pas de liste doit être maintenue de tous les segments de mémoire libre, un seul pointeur vers le haut actuel de la pile. Les compilateurs stockent habituellement ce pointeur dans un registre spécial et rapide à cette fin. 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 on-die du processeur.
(j'ai déplacé cette réponse d'une autre question qui était plus ou moins un dupe de celle-ci.)
la réponse à votre question est spécifique à la mise en œuvre et peut varier selon les compilateurs et les architectures de processeurs. Cependant, voici une explication simplifiée.
- la pile et le tas sont des zones de mémoire attribuées à partir du système d'exploitation sous-jacent (souvent de la mémoire virtuelle qui est associée à la mémoire physique à la demande).
- dans un environnement multi-filetés chaque fil aura sa propre pile complètement indépendante mais ils partageront le tas. L'accès simultané doit être contrôlée sur le tas et n'est pas possible sur la pile.
Le tas
- Le tas contient une liste liée de servir et de blocs libres. Les nouvelles allocations sur le tas (par
new
oumalloc
) sont satisfaites en créant un bloc approprié à partir d'un des blocs libres. Cela nécessite une mise à jour de la liste des blocs sur le tas. Cette méta-informations sur les blocs sur le tas est également stockée sur le tas, souvent, dans une petite zone située juste en face de chaque bloc. - au fur et à mesure que le tas croît, de nouveaux blocs sont souvent attribués à partir d'adresses inférieures vers des adresses supérieures. Ainsi, vous pouvez penser au tas comme un tas de blocs de mémoire qui croît en taille au fur et à mesure que la mémoire est allouée. Si le tas est trop petit pour une allocation la taille peut souvent être augmentée en acquérant plus de mémoire du système d'exploitation sous-jacent.
- allouer et désallouer beaucoup de petits blocs peuvent laisser le tas dans un état où il y a beaucoup de petits blocs libres intercalés entre les blocs utilisés. Une demande d'allouer un gros bloc peut échouer, car aucun des blocs libres sont assez grand pour satisfaire la demande d'allocation, même si la taille combinée des blocs libres peuvent être grandes assez. Cela s'appelle fragmentation tas .
- Lorsqu'un bloc adjacent à un bloc libre est libéré le bloc libre peut être fusionné avec le côté bloc libre pour créer un plus grand bloc libre efficace de réduire la fragmentation du tas.
La pile
- de La pile travaille souvent en étroite tandem avec un registre spécial sur le CPU nommé le pointeur de pile . Au départ, le pointeur de pile pointe vers le haut de la pile (l'adresse la plus élevée sur la pile).
- le CPU a des instructions spéciales pour pousser valeurs sur la pile et popping leur retour de la pile. Chaque push stocke la valeur à l'emplacement actuel du pointeur de pile et diminue la pile pointeur. Un pop récupère la valeur pointée par le pointeur de pile, puis augmente le pointeur de pile (ne soyez pas confus par le fait que ajouter une valeur à la pile diminue le pointeur de pile et enlever une valeur augmente il. Rappelez-vous que la pile atteint le fond). Les valeurs stockées et récupérées sont les valeurs des registres CPU.
- quand une fonction est appelée le CPU utilise des instructions spéciales qui poussent le courant pointeur d'instruction , c'est-à-dire l'adresse du code exécuté sur la pile. Le CPU saute alors à la fonction en pointeur d'instruction à l'adresse de la fonction appelée. Plus tard, lorsque la fonction retourne, la vieille pointeur d'instruction est sorti de la pile et l'exécution reprend le code juste après l'appel à la fonction.
- Lorsqu'une fonction est saisie, le pointeur de pile est diminué pour allouer plus d'espace sur la pile pour les variables locales (automatiques). Si la fonction a une variable locale 32 bits, quatre octets sont mis de côté sur la pile. Lorsque la fonction retourne le pointeur de pile est déplacée vers l'arrière pour libérer la zone allouée.
- Si une fonction a des paramètres, ceux-ci sont poussés sur la pile avant l'appel à la fonction. Le code dans la fonction est alors capable de naviguer vers le haut de la pile à partir du pointeur de pile courant pour localiser ces valeurs.
- la fonction de nidification appelle le travail comme un charme. Chaque nouvel appel affectera les paramètres de la fonction, l'adresse de retour et l'espace pour les variables locales et ces enregistrements d'activation peuvent être empilés pour les appels imbriqués et se détendre de la manière correcte lorsque les fonctions reviennent.
- comme la pile est un bloc de mémoire limité, vous pouvez causer un débordement de la pile en appelant trop de fonctions imbriquées et/ou en allouant trop d'espace pour les variables locales. Souvent, la zone mémoire utilisée pour la pile est configurée de telle sorte que l'écriture en dessous du bas (l'adresse la plus basse) de la pile déclenche un piège ou une exception dans le CPU. Cette condition exceptionnelle peut alors être capturée par l'exécution et convertie en une sorte d'exception de débordement de pile.
peut-on attribuer une fonction sur le tas plutôt qu'une pile?
non, les enregistrements d'activation pour les fonctions (variables locales ou automatiques) sont alloués sur la pile qui est utilisée non seulement pour stocker ces variables, mais aussi pour garder la trace des appels de fonction imbriqués.
Comment le tas est géré est vraiment à l'environnement d'exécution. C utilise malloc
et C++ utilise new
, mais beaucoup d'autres langues ont des déchets collection.
cependant, la pile est une caractéristique de bas niveau étroitement liée à l'architecture du processeur. Faire pousser le tas quand il n'y a pas assez d'espace n'est pas trop difficile car il peut être implémenté dans l'appel de la bibliothèque qui gère le tas. Cependant, la croissance de la pile est souvent impossible car le débordement de la pile n'est découvert que lorsqu'il est trop tard; et l'arrêt du fil d'exécution est la seule option viable.
dans le code C suivant
public void Method1()
{
int i = 4;
int y = 2;
class1 cls1 = new class1();
}
Voici comment la mémoire est gérée
Local Variables
qui ne doivent durer que tant que la fonction invocation va dans la pile. Le tas est utilisé pour les variables dont nous ne connaissons pas vraiment la durée de vie à l'avance, mais nous nous attendons à ce qu'elles durent un certain temps. Dans la plupart des langues, il est essentiel que nous sachions au moment de la compilation Quelle est la taille d'une variable si nous voulons la stocker dans la pile.
(dont la taille varie au fur et à mesure que nous les mettons à jour) vont sur le tas parce que nous ne savons pas au moment de la création combien de temps ils vont durer. Dans de nombreuses langues, le tas est collecté pour trouver des objets (comme l'objet cls1) qui n'ont plus de références.
en Java, la plupart des objets vont directement dans le tas. Dans les langages comme C / C++, Les structures et les classes peuvent souvent rester sur la pile quand il ne s'agit pas pointeur.
Plus d'informations peuvent être trouvées ici:
La différence entre la pile et le tas de l'allocation de mémoire " 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 importants .NET: pile, tas, types de valeur, types de référence, boxe, et unboxing-Codeprojet
mais sachez qu'il peut contenir des inexactitudes.
La Pile Lorsque vous appelez une fonction, les arguments de cette fonction, plus d'autres frais généraux, sont placés sur la pile. Quelques infos (comme l'endroit où aller sur le retour) y sont stockées. Lorsque vous déclarez une variable à l'intérieur de votre fonction, cette variable est également alloué sur la pile.
désallouer la pile est assez simple parce que vous désallouer toujours dans l'ordre inverse dans lequel vous attribuez. Pile de trucs est ajouté comme vous entrez fonctions, les données correspondantes sont supprimés lorsque vous quittez. Cela signifie que vous avez tendance à rester dans une petite région de la pile sauf si vous appelez beaucoup de fonctions qui appellent beaucoup d'autres fonctions (ou créer une solution récursive).
Le Tas Le tas est un nom générique pour où vous mettez les données que vous créer à la volée. Si vous ne savez pas combien de vaisseaux spatiaux votre programme va créer, vous êtes susceptible d'utiliser le nouveau (ou malloc ou équivalent) opérateur pour créer chaque vaisseau spatial. Cette allocation va rester pendant un certain temps, il est donc probable que nous libérerons les choses dans un ordre différent de celui dans lequel nous les avons créées.
ainsi, le tas est beaucoup plus complexe, parce qu'il finit par y avoir des régions de mémoire qui sont inutilisées intercalées avec des morceaux qui sont - la mémoire se fragmente. Trouver la mémoire libre de la taille dont vous avez besoin est un problème difficile. C'est pourquoi le tas doit être évitée (même si c'est encore souvent utilisé).
mise en Œuvre L'implémentation de la pile et de heap est généralement terminée avec le système d'exploitation / runtime. Souvent, les jeux et d'autres applications qui sont critiques sur le plan de la performance créent leurs propres solutions de mémoire qui saisissent une grande partie de la mémoire du tas et la distribuent ensuite à l'interne pour éviter de compter sur L'OS pour la mémoire.
ce n'est pratique que si votre utilisation de la mémoire est très différente de la norme - I. e pour les jeux où vous charger un niveau dans une énorme opération et peut jeter tout le lot dans une autre énorme opération.
emplacement Physique dans la mémoire Ceci est moins pertinent que vous ne le pensez à cause 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 se trouvent ailleurs (même sur le disque dur!). Les adresses que vous obtenez pour la pile sont dans l'ordre croissant que votre l'arbre d'appel devient plus profonde. Les adresses pour le tas sont imprévisibles(I. e mise en œuvre spécifique) et franchement pas important.
afin De préciser, cette réponse a des informations erronées ( thomas fixe sa réponse après les commentaires, cool :) ). Les autres réponses évitent simplement d'expliquer ce que signifie l'attribution statique. Je vais donc vous expliquer les trois principales formes de répartition et comment elles se rapportent habituellement au tas, à la pile et au segment de données ci-dessous. Je montrerai aussi quelques exemples en C / C++ et Python pour aider les gens à comprendre.
"Static" (alias statically les variables ne sont pas attribuées sur la pile. Ne présumez pas - beaucoup de gens le font seulement parce que " statique "ressemble beaucoup à"pile". Ils n'existent 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".
champ d'application se réfère à ce qui suit: certaines parties du code peuvent accéder à une variable. En général, nous pensons à Local scope (accessible uniquement par la fonction courante) versus global scope (accessible partout) bien que scope puisse devenir beaucoup plus complexe.
durée de vie renvoie au moment où une variable est attribuée et désallouée au cours de l'exécution du programme. Habituellement, nous pensons à allocation statique (la variable persistera tout au long de la période duration of the program, making it useful for storing the same information across several function calls) versus automatic allocation (la variable ne persiste qu'au cours d'un seul appel à une fonction, ce qui la rend utile pour stocker des informations qui ne sont utilisées que pendant votre fonction et peuvent être écartées une fois que vous avez terminé) versus dynamic allocation (variables dont la durée est définie à l'exécution, au lieu de compiler le temps comme statique ou automatique).
bien que la plupart des compilateurs et des interprètes appliquent ce comportement de manière similaire en termes d'utilisation de piles, tas, etc, un compilateur peut parfois briser ces conventions s'il le veut aussi longtemps que le comportement est correct. Par exemple, en raison de l'optimisation, une variable locale peut n'exister que dans un registre ou être entièrement supprimée, 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 pile ou tas, mais à la place d'autres mécanismes de stockage (rarement fait, puisque les piles et les tas sont grands pour cela).
je vais vous 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 regarder le comportement. Si vous préférez lire python, sauter à la fin de la réponse :)
// Statically allocated in the data segment when the program/DLL is first loaded
// Deallocated when the program/DLL exits
// scope - can be accessed from anywhere in the code
int someGlobalVariable;
// Statically allocated in the data segment when the program is first loaded
// Deallocated when the program/DLL exits
// scope - can be accessed from anywhere in this particular code file
static int someStaticVariable;
// "someArgument" is allocated on the stack each time MyFunction is called
// "someArgument" is deallocated when MyFunction returns
// scope - can be accessed only within MyFunction()
void MyFunction(int someArgument) {
// Statically allocated in the data segment when the program is first loaded
// Deallocated when the program/DLL exits
// scope - can be accessed only within MyFunction()
static int someLocalStaticVariable;
// Allocated on the stack each time MyFunction is called
// Deallocated when MyFunction returns
// scope - can be accessed only within MyFunction()
int someLocalVariable;
// A *pointer* is allocated on the stack each time MyFunction is called
// This pointer is deallocated when MyFunction returns
// scope - the pointer can be accessed only within MyFunction()
int* someDynamicVariable;
// This line causes space for an integer to be allocated in the heap
// when this line is executed. Note this is not at the beginning of
// the call to MyFunction(), like the automatic variables
// scope - only code within MyFunction() can access this space
// *through this particular variable*.
// However, if you pass the address somewhere else, that code
// can access it too
someDynamicVariable = new int;
// This line deallocates the space for the integer in the heap.
// If we did not write it, the memory would be "leaked".
// Note a fundamental difference between the stack and heap
// the heap must be managed. The stack is managed for us.
delete someDynamicVariable;
// In other cases, instead of deallocating this heap space you
// might store the address somewhere more permanent to use later.
// Some languages even take care of deallocation for you... but
// always it needs to be taken care of at runtime by some mechanism.
// When the function returns, someArgument, someLocalVariable
// and the pointer someDynamicVariable are deallocated.
// The space pointed to by someDynamicVariable was already
// deallocated prior to returning.
return;
}
// Note that someGlobalVariable, someStaticVariable and
// someLocalStaticVariable continue to exist, and are not
// deallocated until the program exits.
un exemple particulièrement poignant de pourquoi il est important de faire la distinction entre la durée de vie et la portée est - ce qu'une variable peut avoir une portée locale mais une durée de vie statique - par exemple, "someLocalStaticVariable" dans l'échantillon de code ci-dessus. De telles variables peuvent rendre nos habitudes de nommage communes mais informelles très déroutantes. Par exemple, lorsque nous disons local " nous voulons habituellement dire " variable attribuée automatiquement et portée localement " et lorsque nous disons global, nous voulons généralement dire " variable attribuée statiquement et portée mondiale ". Malheureusement quand il vient à des choses comme " fichier scoped variables statically allouées " beaucoup de gens disent juste... hein??? ".
certains des choix de syntaxe en C / C++ exacerbent ce problème - par exemple, beaucoup de gens pensent que les variables globales ne sont pas "statiques" à cause de la syntaxe indiquée ci-dessous.
int var1; // Has global scope and static allocation
static int var2; // Has file scope and static allocation
int main() {return 0;}
noter que l'insertion du mot-clé" static " 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 à la place de dire quelque chose comme "fichier" ou "fichier limité" la portée. Cependant, de nombreuses personnes utilisent l'expression "statique" ou "portée statique" pour décrire une variable qui ne peut être consultée qu'à partir d'un seul fichier de code. Dans le contexte de la vie, "statique", toujours signifie que la variable est allouée au démarrage du programme et libéré lorsque le programme sortie.
certains pensent que ces concepts sont spécifiques au c/c++. Ils ne le sont pas. Par exemple, L'échantillon 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 ne vais pas entrer ici).
from datetime import datetime
class Animal:
_FavoriteFood = 'Undefined' # _FavoriteFood is statically allocated
def PetAnimal(self):
curTime = datetime.time(datetime.now()) # curTime is automatically allocatedion
print("Thank you for petting me. But it's " + str(curTime) + ", you should feed me. My favorite food is " + self._FavoriteFood)
class Cat(Animal):
_FavoriteFood = 'tuna' # Note since we override, Cat class has its own statically allocated _FavoriteFood variable, different from Animal's
class Dog(Animal):
_FavoriteFood = 'steak' # Likewise, the Dog class gets its own static variable. Important to note - this one static variable is shared among all instances of Dog, hence it is not dynamic!
if __name__ == "__main__":
whiskers = Cat() # Dynamically allocated
fido = Dog() # Dynamically allocated
rinTinTin = Dog() # Dynamically allocated
whiskers.PetAnimal()
fido.PetAnimal()
rinTinTin.PetAnimal()
Dog._FavoriteFood = 'milkbones'
whiskers.PetAnimal()
fido.PetAnimal()
rinTinTin.PetAnimal()
# Output is:
# Thank you for petting me. But it's 13:05:02.255000, you should feed me. My favorite food is tuna
# Thank you for petting me. But it's 13:05:02.255000, you should feed me. My favorite food is steak
# Thank you for petting me. But it's 13:05:02.255000, you should feed me. My favorite food is steak
# Thank you for petting me. But it's 13:05:02.255000, you should feed me. My favorite food is tuna
# Thank you for petting me. But it's 13:05:02.255000, you should feed me. My favorite food is milkbones
# Thank you for petting me. But it's 13:05:02.256000, you should feed me. My favorite food is milkbones
D'autres ont répondu aux grandes lignes assez bien, donc je vais jeter dans quelques détails.
-
la Pile et le tas n'a pas besoin d'être singulier. 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 fil a sa propre pile. Vous pouvez également avoir plus d'un tas, par exemple, certaines configurations DLL peuvent avoir pour résultat des DLLs différentes, qui se répartissent à partir de différents tas, ce qui explique pourquoi généralement une mauvaise idée de libérer la mémoire allouée par une autre bibliothèque.
-
dans C vous pouvez obtenir l'avantage de l'allocation de longueur variable par l'utilisation de alloca , qui allaite sur la pile, par opposition à allocates, qui allocates sur le tas. Cette mémoire ne survivra pas à votre déclaration de retour, mais elle est utile pour un tampon scratch.
-
faire un énorme tampon temporaire sur Windows que vous n'utilisez pas beaucoup de n'est pas libre. C'est parce que le compilateur va générer une boucle de sonde de pile qui est appelée chaque fois que votre fonction est entrée pour s'assurer que la pile existe (parce que Windows utilise une page de garde unique à la fin de votre pile pour détecter quand il a besoin de faire pousser la pile. Si vous accédez à la mémoire plus d'une page à l'extrémité de la pile, vous vous écraserez). Exemple:
void myfunction()
{
char big[10000000];
// Do something that only uses for first 1K of big 99% of the time.
}
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 allocateurs basés sur mmap()
). La page Web Memory Management Glossary présente un schéma de cette mise en page.
la pile et le tas sont traditionnellement situés aux extrémités opposées de l'espace d'adresse virtuel du processus. La pile augmente automatiquement lors de l'accès, jusqu'à une taille définie par le noyau (qui peut être ajustée avec setrlimit(RLIMIT_STACK, ...)
). Le tas croît lorsque l'allocateur mémoire invoque l'appel système brk()
ou sbrk()
, cartographiant plus de pages de mémoire physique dans l'espace d'adresse virtuelle du processus.
dans les systèmes sans mémoire virtuelle, comme certains systèmes embarqués, la même disposition de base s'applique souvent, sauf que la pile et le tas sont de taille fixe. Toutefois, dans d'autres systèmes embarqués (tels que comme ceux basés sur microcontrôleurs PIC Microchip), la pile de programme est un bloc de mémoire séparé qui n'est pas adressable par les instructions de mouvement de données, et ne peut être modifié ou lu indirectement par les instructions de flux de programme (appel, retour, etc.). D'autres architectures, comme les processeurs Intel Itanium, ont multiple stacks . En ce sens, la pile est un élément de l'architecture CPU.
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 retourner une valeur de la pile) et "push" (pousser une valeur à la pile), mais aussi call (appeler une sous - routine - cela pousse l'adresse à retourner à la pile) et return (retourner d'une sous-routine-cela pop l'adresse hors de la pile et saute à elle). C'est la région de mémoire sous le registre de pointeur de pile, qui peut être réglée au besoin. La pile est aussi utilisé pour passer des arguments à des sous-routines, et aussi pour préserver les valeurs dans les registres avant d'appeler des sous-routines.
le tas est une partie de la mémoire qui est donnée à une application par le système d'exploitation, généralement à travers un syscall comme malloc. Sur les os modernes, cette mémoire est un ensemble de pages auquel seul le processus d'appel a accès.
la taille de la pile est déterminée à l'exécution, et ne croît généralement pas après le programme lancer. Dans un programme C, la pile doit être assez grande pour contenir chaque variable déclarée dans chaque fonction. Le tas croîtra dynamiquement au besoin, mais l'OS finira par faire l'appel (il croîtra 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 dans le noyau pour obtenir plus de mémoire. Ce comportement est souvent personnalisable)
parce que vous avez attribué la pile avant de lancer le programme, vous n'avez jamais besoin de malloc avant de pouvoir utiliser la pile, de sorte que c'est 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, parce que la façon dont les pages sont implémentées et où elles sont stockées est un détail d'implémentation.
je pense que beaucoup d'autres personnes vous ont donné des réponses généralement correctes à ce sujet.
un détail qui a été omis, cependant, est que le" tas "devrait en fait probablement être appelé le"free store". La raison de cette distinction est que le free store original a été mis en œuvre avec une structure de données connue comme un "tas binomial."Pour cette raison, l'allocation des premières implémentations de malloc()/free() était une allocation d'un tas. Cependant, dans cette époque moderne, la plupart des free stores sont implémentés avec des structures de données très élaborées qui ne sont pas des tas binomiaux.
qu'est Ce qu'une pile?
une pile est une pile d'objets, généralement bien agencée.
Piles dans les architectures informatiques sont des zones de mémoire où les données sont ajoutées ou supprimées dans un last-in-first-out).
Dans une application multi-threadée, chaque thread avoir sa propre pile.
Qu'est-ce qu'un tas?
un tas est une collection désordonnée de choses empilées au hasard.
dans les architectures de calcul, le tas est une zone de mémoire allouée dynamiquement qui est gérée automatiquement par le système d'exploitation ou le gestionnaire de mémoire de la bibliothèque.
La mémoire sur le tas est attribuée, désallouée, et redimensionnée régulièrement pendant l'exécution du programme, et cela peut conduire à un problème appelé fragmentation.
La Fragmentation se produit lorsque des objets mémoire sont alloués avec de petits espaces entre eux qui sont trop petits pour contenir des objets mémoire supplémentaires.
Le résultat net est un pourcentage de l'espace tas qui n'est pas utilisable pour la mémoire supplémentaire allocation.
les deux ensemble
dans une application multi-threadée, chaque fil aura sa propre pile. Mais, tous les fils différents partageront le tas.
Parce que les différents threads partagent le tas dans une application multi-threads, cela signifie également qu'il doit y avoir une certaine coordination entre les threads afin qu'ils n'essayez pas d'accéder et de manipuler les mêmes pièces de mémoire dans le tas en même temps.
qui est le plus rapide – la pile ou le tas? Et pourquoi?
la pile est beaucoup plus rapide que le tas.
C'est à cause de la façon dont la mémoire est allouée sur la pile.
Allouer la mémoire sur la pile est aussi simple que de déplacer le pointeur de pile.
pour les personnes novices en programmation, c'est probablement une bonne idée d'utiliser la pile car c'est plus facile.
Parce que la pile est faible, vous pouvez l'utiliser lorsque vous savez exactement quelle quantité de mémoire dont vous aurez besoin pour vos données, ou si vous connaissez la taille de vos données est très faible.
Il est préférable d'utiliser le tas quand vous savez que vous aurez besoin de beaucoup de la mémoire de vos données, ou si vous n'êtes pas sûr de combien de mémoire vous aurez besoin (comme avec un tableau dynamique).
Java Modèle De Mémoire
la pile est la zone de mémoire où les variables locales (y compris les paramètres de la méthode) sont stockées. Quand il s'agit de variables Objet, Ce sont simplement des références (des pointeurs) aux objets réels sur le tas.
Chaque fois qu'un objet est instancié, un morceau de mémoire tas est mis de côté pour contenir les données (état) de cet objet. Puisque les objets peuvent contenir d'autres objets, certaines de ces données peuvent en fait contenir des références à ces objets imbriqués.
Vous 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 abondants 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 du tas, et vous dépassez les limites de votre bloc alloué, vous avez une bonne chance de déclencher une défaillance de segment. (Pas 100%: votre bloc peut être contigu à un autre que vous avez déjà attribué.) Mais puisque les variables créées sur la pile sont toujours contiguës les unes aux autres, l'écriture hors limites peut changer la valeur d'une autre variable. J'ai appris que chaque fois que j'ai l'impression que mon programme a cessé d'obéir aux lois de la logique, c'est probablement un débordement de tampon.
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 programmes (pointeur vers l'instruction machine suivante) 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à). Quand le sous-programme est terminé, tout est sorti de la pile. Le PC et les données de registre obtient et remet où il était comme il est pour que votre programme continue son chemin.
le tas est le domaine de la mémoire dynamique les allocations de mémoire sont faites à partir d'appels" nouveaux "ou" d'allocation " explicites. Il s'agit d'une structure de données spéciale qui permet de suivre les blocs de mémoire de différentes tailles et leur statut d'allocation.
dans" classique " systèmes RAM a été conçu de sorte que le pointeur de pile a commencé au bas de la mémoire, le pointeur de tas a commencé au sommet, et ils se sont développés vers mutuellement. S'ils se chevauchent, vous êtes hors de RAM. Mais ça ne marche pas avec les os multi-threads modernes. Chaque fil doit avoir sa propre pile, et ceux-ci peuvent être créés dynamiquement.
De WikiAnwser.
Pile
Lorsqu'une fonction ou une méthode appelle une autre fonction qui s'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, parce que les éléments de la pile (appels de fonction) dépendent les uns des autres.
la pile est importante pour considérez dans la manipulation d'exception et les exécutions de fil.
tas
le tas est simplement la mémoire utilisée par les programmes pour stocker des variables. Les éléments du tas (variables) n'ont pas de dépendances entre eux et peuvent toujours être consultés au hasard à tout moment.
Pile
- accès très rapide
- N'ont pas explicitement de l'affectation des variables
- L'espace est géré efficacement par CPU, la mémoire ne sera pas fragmentée
- variables locales seulement
- Limite sur la taille de la pile (dépendants)
- les Variables ne peuvent pas être redimensionnées
tas
- les Variables sont accessibles à l'échelle mondiale
- Pas de limite sur la taille de la mémoire
- accès (relativement) plus lent
- aucune garantie d'utilisation efficace de l'espace, la mémoire peut se fragmenter au fil du temps à mesure que des blocs de mémoire sont alloués, puis libérés
- Vous devez gérer la mémoire (vous êtes en charge d'allouer et de libérer des variables) Les Variables
- peuvent être redimensionnées en utilisant realloc ()
dans les années 1980, UNIX s'est propagé comme des lapins avec de grandes entreprises qui roulaient les leurs. Exxon en avait un, ainsi que des dizaines de marques perdues dans l'histoire. La façon dont la mémoire était présentée était à la discrétion des nombreux réalisateurs.
un programme C typique a été présenté à plat dans la mémoire avec une opportunité d'augmenter en changeant la valeur de brk (). Typiquement, le tas était juste en dessous de cette valeur brk et l'augmentation de brk a augmenté la quantité de tas disponibles.
La pile simple était typiquement une zone en dessous de tas qui était un tract de mémoire ne contenant rien de valeur jusqu'au sommet du bloc de mémoire fixe suivant. Ce bloc suivant était souvent du CODE qui pouvait être écrasé par des données de pile dans l'une des célèbres hacks de son époque.
un bloc mémoire typique était BSS (un bloc de valeurs zéro) qui n'a pas été accidentellement mis à zéro dans l'offre d'un fabricant. Une autre était des données contenant des valeurs initialisées, y compris des chaînes et des nombres. Un le troisième était le CODE contenant CRT (C runtime), main, functions, et libraries.
l'avènement de la mémoire virtuelle dans UNIX change beaucoup de contraintes. Il n'y a aucune raison objective pour laquelle ces blocs doivent être contigus, ou fixe dans la taille, ou commandé un particulier maintenant. Bien sûr, avant UNIX était Multics qui n'a pas souffert de ces contraintes. Voici un schéma montrant l'un de la mémoire des schémas de l'époque.
- Introduction
la mémoire physique est l'étendue des adresses physiques des cellules de mémoire dans lesquelles une application ou un système stocke ses données, son code, et ainsi de suite pendant l'exécution. La gestion de la mémoire désigne la gestion de ces adresses physiques par le transfert des données de la mémoire physique vers un périphérique de stockage, puis retour à la mémoire physique si nécessaire. L'OS implémente les services de gestion de la mémoire en utilisant la mémoire virtuelle. En tant que développeur d'applications C#, vous n'avez pas besoin d'écrire de services de gestion de mémoire. Le CLR utilise les services sous-jacents de gestion de mémoire OS pour fournir le modèle de mémoire pour C# ou tout autre langage de haut niveau ciblant le CLR.
la Figure 4-1 montre la mémoire physique qui a été abstraite et gérée par L'OS, en utilisant le concept de mémoire virtuelle. La mémoire virtuelle est la vue abstraite de la mémoire physique, géré par l'OS. La mémoire virtuelle est tout simplement série d'adresses virtuelles, et ces adresses virtuelles sont traduites par le CPU dans l'adresse physique si nécessaire.
la Figure 4-1. CLR de la mémoire de l'abstraction
le CLR fournit la couche abstraite de gestion de mémoire pour l'environnement d'exécution virtuelle, en utilisant les services de mémoire opérationnelle. Les concepts abstraits utilisés par CLR sont AppDomain, thread, stack, heapmemorymapped file, et ainsi de suite. Le concept du domaine d'application (AppDomain) donne à votre application un environnement d'exécution isolé.
- la Mémoire de l'Interaction entre le CLR et d'OS
en regardant la trace de la pile lors du débogage de l'application C # suivante, en utilisant WinDbg, vous verrez comment le CLR utilise les services sous-jacents de gestion de mémoire OS (par exemple, la méthode HeapFree de KERNEL32.dll, la méthode RtlpFreeHeap de ntdll.dll) pour mettre en œuvre son propre modèle de mémoire:
using System;
namespace CH_04
{
class Program
{
static void Main(string[] args)
{
Book book = new Book();
Console.ReadLine();
}
}
public class Book
{
public void Print() { Console.WriteLine(ToString()); }
}
}
l'assemblage compilé du programme est chargé dans WinDbg pour commencer le débogage. Vous utilisez la commande suivante commandes pour initialiser la session de débogage:
0: 000 > sxe ld clrjit
0: 000> g
0: 000> .loadby sos clr
0: 000> .charge C:\Windows\Microsoft.NET\Framework\v4.0.30319\sos.dll
ensuite, vous définissez un point de rupture à la méthode principale de la classe de programme, en utilisant le !commande bpmd:
0: 000>!bpmd CH_04.exe CH_04.Programme.Principal
pour poursuivre l'exécution et briser au point de rupture, exécuter le g commande:
0: 000> g
quand l'exécution se brise au point de rupture, vous utilisez l' !la commande eestack pour voir les détails des traces de la pile de tous les threads en cours d'exécution pour le processus en cours. La sortie suivante montre la trace de la pile pour tous les threads tournant pour L'application CH_04.exe:
0: 000>!eestack
Thread 0
sur l'image Actuelle: (MethodDesc 00233800 +0 CH_04.Programme.Principale(Système D'.String [])
ChildEBP RetAddr Caller, Calée
0022ed24 5faf21db clr!Calldescr worker+0x33
/ trace enlevée /
0022f218 77712d68 ntdll!RtlFreeHeap+0x142, calling ntdll!RtlpFreeHeap
0022f238 771df1ac KERNEL32!HeapFree+0x14, l'appel de ntdll!RtlFreeHeap
0022f24c 5fb4c036 clr!EEHeapFree+0x36, appel KERNEL32!HeapFree
0022f260 5fb4c09d clr!EEHeapFreeInProcessHeap+0x24, calling clr!EEHeapFree
0022f274 5fb4c06d clr!opérateur Supprimer []+0x30, appel clr!Eeheapfreeinprocessheap / trace removed /
0022f4d0 7771316f ntdll!RtlpFreeHeap+0xb7a, calling ntdll!_seh_pilog4
0022f4d4 77712d68 ntdll!RtlFreeHeap+0x142, calling ntdll!RtlpFreeHeap
0022f4f4 771df1ac KERNEL32!HeapFree+0x14, l'appel de ntdll!RtlFreeHeap
/ trace enlevée /
cette trace de pile indique que le CLR utilise des services de gestion de mémoire OS pour implémenter son propre modèle de mémoire. Toute opération de mémoire in.NET passe par la couche mémoire CLR à la couche gestion de la mémoire OS.
la Figure 4-2 illustre un modèle type de mémoire d'application C# utilisé par le CLR à l'exécution.
Figure 4-2 . Un modèle typique de mémoire d'application C#
le modèle de mémoire CLR est étroitement couplé avec les services de gestion de mémoire OS. Pour comprendre le modèle mémoire CLR, il est important de comprendre le modèle mémoire OS sous-jacent. Il est également crucial de savoir comment l'adresse de mémoire physique de l'espace, est placé dans l'espace d'adressage mémoire virtuelle, les manières de l'adresse virtuelle l'espace est utilisé par l'application utilisateur et l'application système, Comment fonctionne la cartographie des adresses virtuelles à physiques, comment fonctionne le fichier cartographié en mémoire, et ainsi de suite. Cette connaissance de base améliorera votre compréhension des concepts de modèles de mémoire CLR, y compris AppDomain, stack, et heap.
Pour plus d'informations, reportez-vous à ce livre:
C# Déconstruit: Découvrez comment C# fonctionne sur la .NET Framework
Ce livre + ClrViaC# + Windows Internals sont d'excellentes ressources connues .net framework en profondeur et en relation avec les OS.
En Sorte
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 mémoire vive 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 très près. Chaque fois qu'une fonction déclare une nouvelle variable, elle est "poussé" sur la pile. Ensuite, à chaque sortie d'une fonction, 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, c'est que la mémoire est gérée pour vous. Vous ne devez pas allouer la mémoire à la main, ou 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 façon efficace, la lecture et l'écriture des variables de pile est très rapide.
plus de 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 gérée aussi étroitement par le CPU. C'est une région de mémoire plus libre-flottante (et elle est 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 d'utiliser 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 qu'on appelle une fuite de mémoire. C'est, de la mémoire sur le tas sera toujours mis de côté (et ne sera pas disponible à d'autres processus). Comme nous le verrons dans le débogage section, Il ya 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 (à l'exception des limites physiques évidentes de votre ordinateur). La mémoire 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 allons parler de pointeurs sous peu.
contrairement à la pile, les variables créé sur le tas sont accessibles par n'importe quelle fonction, n'importe où dans votre programme. Les variables du tas ont une portée essentiellement globale.
plus de 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 attribution est traitée lorsque le programme est compilé. Lorsqu'une fonction ou une méthode appelle une autre fonction qui s'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 la trace de la pile, libérer un bloc de la pile n'est rien de plus que d'ajuster un pointeur.
Variables allouées sur le tas ont leur mémoire allouée à l'exécution le temps 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 pas de liens de dépendance entre eux et peuvent toujours être consultés au hasard à tout moment. Vous pouvez allouer un bloc à tout moment et gratuitement à tout moment. Il est donc beaucoup plus complexe de savoir quelles parties du tas sont attribuées ou gratuites à un moment donné.
vous pouvez utiliser la pile si vous savez exactement combien de données vous devez allouer avant le temps de compilation, et ce n'est pas trop gros. 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 une grande quantité de données.
dans une situation multi-threadée 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 manipulation d'exception et les exécutions de filetage.
chaque thread reçoit une pile, alors qu'il n'y a généralement qu'un tas pour l'application (bien qu'il ne soit pas rare d'avoir plusieurs tas pour différents types d'allocation).
à l'exécution, si l'application a besoin de plus de tas, il peut allouer la mémoire de la mémoire libre et si la pile a besoin de mémoire, il peut attribuez la mémoire de la mémoire libre attribuée à la mémoire pour l'application.
plus de détails sont donnés ici et ici .
viens Maintenant à l' votre question réponses .
dans quelle mesure sont-ils contrôlés par le système D'exploitation ou le langage runtime?
L'OS affecte la pile pour chaque thread au niveau du système lorsque le thread est créé. Typiquement, L'OS est appelé par la langue runtime pour allouer le tas pour l'application.
plus de ici .
Quelle est leur portée?
déjà indiqué en haut.
" vous pouvez utiliser la pile si vous savez exactement combien de données vous devez allouer avant le temps de compilation, et ce n'est pas trop gros. 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 une grande quantité de données."
Plus peut être trouvé dans ici .
Qu'est-ce qui détermine la taille de chacun d'eux?
La taille de la pile est définie par OS lorsqu'un thread est créé. La taille de la pile est définie au démarrage de l'application, mais elle peut augmenter en fonction de l'espace requis (l'allocateur demande plus de mémoire à partir du système d'exploitation).
Qu'est-ce qui rend plus rapide?
attribution de pile est beaucoup plus rapide car tout ce qu'il fait vraiment est de se déplacer le pointeur de pile. En utilisant des pools de mémoire, vous pouvez obtenir des performances comparables sur l'allocation de tas, mais cela vient avec une légère complexité ajoutée et ses propres maux de tête.
en outre, stack vs. heap n'est pas seulement une considération de performance; il vous en dit aussi beaucoup sur la durée de vie attendue des objets.
les détails peuvent être trouvés dans ici .
OK, simplement et en bref, ils signifient commandé et non commandé ...!
Stack : dans stack items, les choses obtenir sur le dessus de l'autre-l'autre, signifie va être plus rapide et plus efficace pour être traitée!...
donc il y a toujours un index pour pointer l'article spécifique, aussi le traitement va être plus rapide, il y a une relation entre les articles comme bien!...
tas : aucune commande, le traitement va être plus lent et les valeurs sont faussées ensemble avec aucun ordre ou index spécifique... il y a du hasard et il n'y a pas de relation entre eux... le temps d'exécution et d'utilisation peut donc varier...
je crée aussi l'image ci-dessous pour montrer à quoi elles peuvent ressembler:
un couple de cents: je pense, il sera bon de dessiner la mémoire graphique et plus simple:
Flèches-montrer où la croissance de la pile et tas, la taille de la pile de processus ont limite, défini dans OS, thread les limites de taille de la pile par les paramètres dans thread créer API habituellement. Tas limitant habituellement par processus la taille maximale de la mémoire virtuelle, par exemple pour 32 bits 2-4 GO.
façon si simple: le tas de processus est général pour le processus et tous les fils à l'intérieur, en utilisant pour l'allocation de mémoire dans le cas commun avec quelque chose comme malloc() .
pile est mémoire rapide pour stocker dans la fonction de cas commun pointeurs de retour de fonction et variables, traitées comme des paramètres dans l'appel de fonction, variables de fonction locales.
depuis que certaines réponses sont allées de Travers, je vais contribuer mon acarien.
étonnamment, personne n'a mentionné que les piles d'appels multiples (c'est-à-dire sans rapport avec le nombre de threads de niveau OS) se trouvent non seulement dans les langues exotiques (PostScript) ou les plateformes (Intel Itanium), mais aussi dans les fibres , Green threads et quelques implémentations de coroutines .
Fibres, Fils verts et coroutines sont à bien des égards similaires, ce qui conduit à beaucoup de confusion. La différence entre les fibres et les fils verts est que les premiers utilisent multitâche coopérative, tandis que les derniers peuvent présenter soit coopérative ou préemptive un (ou même les deux). Pour la distinction entre fibres et coroutines, voir ici .
dans tous les cas, le but des deux fibres, fils verts et coroutines a de multiples fonctions d'exécution concurremment, mais pas en parallèle (voir cette question SO pour la distinction) à l'intérieur d'un seul fil OS-niveau, le transfert de contrôle de l'un à l'autre d'une manière organisée.
lorsque vous utilisez des fibres, des fils verts ou des coroutines, vous habituellement ont une pile séparée par fonction. (Techniquement, pas seulement une pile, mais un ensemble de contexte d'exécution est par fonction. Plus important encore, les registres CPU.) Pour chaque thread, il y a autant de piles qu'il y a de fonctions en cours d'exécution, et le thread change entre l'exécution de chaque fonction selon la logique de votre programme. Quand une fonction court à sa fin, sa pile est détruite. Ainsi, le nombre et les durées de vie des piles sont dynamiques et ne sont pas déterminés par le nombre de fils au niveau OS!
Notez que j'ai dit " habituellement ont une pile séparée par fonction". Il y a à la fois empilable et empilable implémentations de couroutines. Les implémentations de C++ les plus notables sont Boost.Corutine et Microsoft PPL 's async/await
. (Cependant, C++' reprise de fonctions (un.k.un. " async
et await
"), qui ont été proposés à C++17, sont susceptibles d'utiliser des coroutines empilées.)
Fibres proposition de la norme C++ de la bibliothèque est à venir. Il y a aussi des bibliothèques "1519520920 . Les fils verts sont extrêmement populaires dans des langues comme Python et Ruby.
j'ai quelque chose à partager avec vous, bien que les principaux points soient déjà écrits.
Pile
- accès très rapide.
- stocké en mémoire vive. Les appels de fonction
- sont chargés ici avec les variables locales et les paramètres de fonction passés.
- espace est libéré automatiquement lorsque le programme sort d'une portée.
- stocké dans mémoire séquentielle.
tas
- accès lent comparativement à la pile.
- stocké en mémoire vive.
- les variables créées dynamiquement sont stockées ici, ce qui nécessite plus tard de libérer la mémoire allouée après utilisation.
- stocké partout où l'allocation de mémoire est faite, accessible par pointeur toujours.
Note intéressante:
- si les appels de fonction avaient été stockés dans le tas, il en aurait résulté 2 points de désordre:
- grâce au stockage séquentiel en pile, l'exécution est plus rapide. Le stockage dans heap aurait entraîné une consommation de temps énorme résultant ainsi le programme entier à exécuter plus lent.
- si les fonctions étaient stockées dans heap (désordre de stockage pointé par pointeur), il n'y aurait eu aucun moyen de revenir à l'adresse de retour de l'appelant (que la pile donne en raison du stockage séquentiel dans la mémoire).
les Retours d'expérience sont wellcomed.
beaucoup de réponses sont correctes en tant que concepts, mais il faut noter qu'une pile est nécessaire au matériel (i.e. microprocesseur) pour permettre l'appel de sous-routines (CALL in assembly language)..). (Programmation orientée objet, les gars vont l'appeler méthodes )
sur la pile, vous enregistrez les adresses de retour et l'appel → push / ret → pop est géré directement dans le matériel.
vous pouvez utiliser la pile pour passer les paramètres.. même si il est plus lent que l'utilisation des registres (serait un gourou du microprocesseur say ou un bon BIOS des années 80...)
- sans pile Non un microprocesseur peut fonctionner. (nous ne pouvons pas imaginer un programme, même en langage assembleur, sans sous-programmes/fonctions)
- sans le tas qu'il peut. (Un programme de langage d'assemblage peut fonctionner sans, comme le tas est un concept OS, comme malloc, qui est un appel OS/Lib.
L'utilisation de la pile est plus rapide que:
- est matériel, et même push/pop sont très efficaces.
- malloc nécessite l'entrée en mode noyau, l'utilisation de serrure/sémaphore (ou d'autres primitives de synchronisation) l'exécution d'un certain code et la gestion de certaines structures nécessaires pour garder la trace de l'allocation.