web-dev-qa-db-fra.com

Quelles sont les différences entre VirtualAlloc et HeapAlloc?

Il existe de nombreuses méthodes d'allocation de mémoire dans l'environnement Windows, telles que VirtualAlloc, HeapAlloc, malloc, new.

Quelle est donc la différence entre eux?

75
user53670

Chaque API est destinée à des usages différents. Chacun requiert également que vous utilisiez la fonction de désallocation/libération correcte lorsque vous avez terminé avec la mémoire.

VirtualAlloc

Une API Windows de bas niveau qui offre de nombreuses options, mais qui est principalement utile pour les personnes dans des situations assez spécifiques. Peut uniquement allouer de la mémoire dans (modifier: pas 4 Ko) de plus gros morceaux. Il y a des situations où vous en avez besoin, mais vous saurez quand vous êtes dans l'une de ces situations. L'une des plus courantes consiste à partager la mémoire directement avec un autre processus. Ne l'utilisez pas pour l'allocation de mémoire à usage général. Utilisez VirtualFree pour désallouer.

HeapAlloc

Alloue la taille de mémoire que vous demandez, pas en gros morceaux que VirtualAlloc. HeapAlloc sait quand il doit appeler VirtualAlloc et le fait automatiquement pour vous. Comme malloc, mais est uniquement Windows et offre quelques options supplémentaires. Convient pour allouer des morceaux généraux de mémoire. Certaines API Windows peuvent exiger que vous l'utilisiez pour allouer de la mémoire que vous leur transmettez, ou que vous utilisiez son compagnon HeapFree pour libérer de la mémoire qu'elles vous renvoient.

malloc

La façon C d'allouer de la mémoire. Préférez-le si vous écrivez en C plutôt qu'en C++ et que vous souhaitez que votre code fonctionne, par exemple Les ordinateurs Unix aussi, ou quelqu'un dit spécifiquement que vous devez l'utiliser. N'initialise pas la mémoire. Convient pour allouer des morceaux généraux de mémoire, comme HeapAlloc. Une API simple. Utilisez free pour désallouer. malloc de Visual C++ _ appelle HeapAlloc.

nouveau

La façon C++ d'allouer de la mémoire. Préférez-le si vous écrivez en C++. Il place également un objet ou des objets dans la mémoire allouée. Utilisez delete pour désallouer (ou delete[] Pour les tableaux). new de Visual studio _ appelle HeapAlloc, puis initialise peut-être les objets, selon la façon dont vous l'appelez.

Dans les standards C++ récents (C++ 11 et supérieur), si vous devez utiliser manuellement delete, vous vous trompez et devez utiliser un pointeur intelligent comme unique_ptr au lieu. A partir de C++ 14, la même chose peut être dite de new (remplacé par des fonctions telles que make_unique()).


Il existe également quelques autres fonctions similaires comme SysAllocString que l'on peut vous dire que vous devez utiliser dans des circonstances spécifiques.

79
Doug

VirtualAlloc est une allocation spécialisée du système de mémoire virtuelle (VM) du système d'exploitation. Les allocations dans le système VM doivent être effectuées à une granularité d'allocation qui (la granularité d'allocation) dépend de l'architecture. L'allocation dans le système VM est l'une des formes les plus élémentaires d'allocation de mémoire. VM les allocations peuvent prendre plusieurs formes, la mémoire n'est pas nécessairement dédiée ou physiquement sauvegardée dans RAM (bien qu'elle puisse l'être). VM l'allocation est généralement un type d'allocation spécial, soit parce que l'allocation doit

  • être très grand,
  • doit être partagé,
  • doit être aligné sur une valeur particulière (raisons de performance) ou
  • l'appelant n'a pas besoin d'utiliser toute cette mémoire à la fois ...
  • etc...

HeapAlloc est essentiellement ce que malloc et new finissent par appeler. Il est conçu pour être très rapide et utilisable dans de nombreux types de scénarios différents d'une allocation à usage général. C'est le "tas" dans un sens classique. Les tas sont en fait configurés par un VirtualAlloc, qui est utilisé pour initialement réserver l'espace d'allocation du système d'exploitation. Une fois l'espace initialisé par VirtualAlloc, divers tableaux, listes et autres structures de données sont configurés pour maintenir et contrôler le fonctionnement du HEAP. Une partie de cette opération prend la forme d'un dimensionnement dynamique (croissance et réduction) du tas, une adaptation du tas à des usages particuliers (allocations fréquentes d'une certaine taille), etc.

new et malloc sont un peu les mêmes, malloc est essentiellement un appel exact à HeapAlloc( heap-id-default ); new cependant, peut [en plus] configurer la mémoire allouée pour C++ objets. Pour un objet donné, C++ stockera des vtables sur le tas pour chaque appelant. Ces vtables sont des redirections pour l'exécution et font partie de ce qui donne à C++ ses OO caractéristiques comme l'héritage, la surcharge de fonctions, etc ...

Certaines autres méthodes d'allocation courantes comme _alloca() et _malloca() sont basées sur stack; Les mappages de fichiers sont réellement alloués avec VirtualAlloc et définis avec des indicateurs de bits particuliers qui désignent ces mappages comme étant de type FILE.

La plupart du temps, vous devez allouer de la mémoire d'une manière compatible avec l'utilisation de cette mémoire;). new en C++, malloc pour C, VirtualAlloc pour les cas massifs ou IPC.

*** Remarque, les grandes allocations de mémoire effectuées par HeapAlloc sont en fait expédiées à VirtualAlloc après une certaine taille (quelques centaines de k ou 16 Mo ou quelque chose que j'oublie, mais assez gros :)).

*** EDIT J'ai brièvement fait une remarque à propos de IPC et VirtualAlloc, il y a aussi quelque chose de très net à propos d'un VirtualAlloc connexe dont aucun des répondants à cette question n'a discuté.

VirtualAllocEx est ce qu'un processus peut utiliser pour allouer de la mémoire dans un espace d'adressage d'un processus différent. Le plus souvent, cela est utilisé en combinaison pour obtenir une exécution à distance dans le contexte d'un autre processus via CreateRemoteThread (similaire à CreateThread, le thread est simplement exécuté dans l'autre processus).

28
RandomNickName42

Il est très important de comprendre la distinction entre les API d'allocation de mémoire (sous Windows) si vous prévoyez d'utiliser un langage qui nécessite une gestion de la mémoire (comme C ou C++.) Et la meilleure façon de l'illustrer à mon humble avis est avec un diagramme:

enter image description here

Notez qu'il s'agit d'une vue très simplifiée, spécifique à Windows.

La façon de comprendre ce diagramme est que plus la méthode d'allocation de mémoire est élevée sur le diagramme, plus l'implémentation niveau supérieur qu'elle utilise. Mais commençons par le bas.

Gestionnaire de mémoire en mode noyau

Il fournit toutes les réservations et allocations de mémoire pour le système d'exploitation, ainsi que la prise en charge de fichiers mappés en mémoire, mémoire partagée, copy-on -write opérations, etc. Ce n'est pas directement accessible depuis le code en mode utilisateur, donc je vais le sauter ici.

VirtualAlloc / VirtualFree

Ce sont les niveau le plus bas API disponibles à partir du mode utilisateur . La fonction VirtualAlloc appelle essentiellement ZwAllocateVirtualMemory qui à son tour fait un rapide syscall to ring0 pour reléguer le traitement ultérieur au gestionnaire de mémoire du noyau. C'est également la méthode la plus rapide pour réserver/allouer un bloc de nouvelle mémoire à partir de tous les disponibles en mode utilisateur.

Mais cela vient avec deux conditions principales:

  • Il alloue uniquement des blocs de mémoire alignés sur la limite de granularité du système.

  • Il alloue uniquement des blocs de mémoire dont la taille est le multiple de la granularité du système.

Alors, quelle est cette granularité du système? Vous pouvez l'obtenir en appelant GetSystemInfo . Il est renvoyé comme paramètre dwAllocationGranularity . Sa valeur est spécifique à l'implémentation (et éventuellement au matériel), mais sur de nombreux systèmes Windows 64 bits, elle est définie sur 0x10000 octets ou 64K.

Donc, tout cela signifie que si vous essayez d'allouer, dites simplement un bloc de mémoire de 8 octets avec VirtualAlloc:

void* pAddress = VirtualAlloc(NULL, 8, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);

En cas de succès, pAddress sera aligné sur le 0x10000 limite d'octets. Et même si vous n'avez demandé que 8 octets, le bloc de mémoire réel que vous obtiendrez sera le page entier (ou quelque chose comme 4K octets. La taille de page exacte est renvoyée dans le paramètre dwPageSize .) Mais, en plus de cela, le bloc de mémoire entier s'étendant sur 0x10000 octets (ou 64K dans la plupart des cas) de pAddress ne sera pas disponible pour d'autres allocations. Donc, en un sens, en allouant 8 octets, vous pourriez aussi bien demander 65536.

Donc, la morale de l'histoire ici n'est pas de substituer VirtualAlloc aux allocations de mémoire génériques dans votre application. Il doit être utilisé pour des cas très spécifiques, comme cela se fait avec le tas ci-dessous. (Généralement pour réserver/allouer de gros blocs de mémoire.)

Une mauvaise utilisation de VirtualAlloc peut entraîner une grave fragmentation de la mémoire.

HeapCreate / HeapAlloc / HeapFree / HeapDestroy

En résumé, les fonctions heap sont essentiellement un wrapper pour la fonction VirtualAlloc. D'autres réponses ici en fournissent un assez bon concept. J'ajouterai que, dans une vue très simpliste, la façon dont tas fonctionne est la suivante:

  • HeapCreate réserve un grand bloc de mémoire virtuelle en appelant VirtualAlloc en interne (ou ZwAllocateVirtualMemory pour être précis). Il met également en place une structure de données interne qui peut suivre d'autres allocations de taille plus petite dans le bloc réservé de mémoire virtuelle.

  • Tous les appels à HeapAlloc et HeapFree n'allouent/libèrent en fait aucune nouvelle mémoire (à moins, bien sûr, que la demande dépasse ce qui a déjà été réservé dans HeapCreate) mais à la place ils = mètre dehors (ou commit) un gros morceau précédemment réservé, en le disséquant en blocs de mémoire plus petits qu'un utilisateur demande.

  • HeapDestroy appelle à son tour VirtualFree qui libère réellement la mémoire virtuelle.

Tout cela fait donc de - tas des fonctions parfaites pour les allocations de mémoire génériques dans votre application. Il est idéal pour les allocations de mémoire de taille arbitraire. Mais un petit prix à payer pour la commodité des fonctions heap est qu'elles introduisent une légère surcharge par rapport à VirtualAlloc lors de la réservation de blocs de mémoire plus importants.

Une autre bonne chose à propos de heap est que vous n'avez pas vraiment besoin d'en créer un. Il est généralement créé pour vous lorsque votre processus démarre. On peut donc y accéder en appelant la fonction GetProcessHeap .

malloc /gratuit

Est un wrapper spécifique au langage pour les fonctions heap. Contrairement à HeapAlloc, HeapFree, etc., ces fonctions fonctionneront non seulement si votre code est compilé pour Windows, mais aussi pour d'autres systèmes d'exploitation (tels que Linux, etc.)

Il s'agit d'une méthode recommandée pour allouer/libérer de la mémoire si vous programmez en C. (Sauf si vous codez un pilote de périphérique en mode noyau spécifique.)

nouvea /supprimer

Venez en tant que haut niveau (enfin, pour C++) opérateurs de gestion de la mémoire. Ils sont spécifiques pour le C++ langue, et comme malloc pour C, sont également les wrappers des fonctions heap. Ils ont également tout un tas de leur propre code qui traite C++- initialisation spécifique des constructeurs, désallocation dans les destructeurs, levée d'une exception, etc.

Ces fonctions sont un moyen recommandé d'allouer/libérer de la mémoire et des objets si vous programmez en C++.


Enfin, je voudrais faire un commentaire sur ce qui a été dit dans d'autres réponses sur l'utilisation de VirtualAlloc pour partager la mémoire entre les processus. VirtualAlloc ne permet pas à lui seul de partager sa mémoire réservée/allouée avec d'autres processus. Pour cela, il faut utiliser CreateFileMapping API qui peut créer un bloc de mémoire virtuelle nommé qui peut être partagé avec d'autres processus. Il peut également mapper un fichier sur le disque dans la mémoire virtuelle pour un accès en lecture/écriture. Mais c'est un autre sujet.

23
ahmd0

En résumé:

  • VirtualAlloc, HeapAlloc etc. sont des API Windows qui allouent directement la mémoire de différents types à partir du système d'exploitation. VirtualAlloc gère les pages du système de mémoire virtuelle Windows, tandis que HeapAlloc alloue à partir d'un segment de système d'exploitation spécifique. Franchement, il est peu probable que vous ayez besoin d'utiliser l'un ou l'autre.

  • malloc est une fonction de bibliothèque Standard C (et C++) qui alloue de la mémoire à votre processus. Les implémentations de malloc utilisent généralement l'une des API du système d'exploitation pour créer un pool de mémoire au démarrage de votre application, puis l'allouer à mesure que vous effectuez des requêtes malloc

  • new est un opérateur C++ standard qui alloue de la mémoire et appelle ensuite les constructeurs de manière appropriée sur cette mémoire. Il peut être implémenté en termes de malloc ou en termes d'API OS, auquel cas, il créera également généralement un pool de mémoire au démarrage de l'application.

7
anon

VirtualAlloc ===> sbrk() sous UNIX

HeapAlloc ====> malloc() sous UNIX

4
Mrad Chems Eddine

VirtualAlloc => Alloue directement dans la mémoire virtuelle, vous réservez/validez en blocs. Ceci est idéal pour les grandes allocations, par exemple les grands tableaux.

HeapAlloc/new => alloue la mémoire sur le tas par défaut (ou tout autre tas que vous pouvez créer). Cela alloue par objet et est idéal pour les petits objets. Le tas par défaut est sérialisable, il a donc une allocation de thread garantie (cela peut provoquer des problèmes sur les scénarios à hautes performances et c'est pourquoi vous pouvez créer vos propres tas).

malloc => utilise le tas d'exécution C, similaire à HeapAlloc mais il est courant pour les scénarios de compatibilité.

En un mot, le tas est juste un morceau de mémoire virtuelle qui est régi par un gestionnaire de tas (plutôt que de la mémoire virtuelle brute)

Le dernier modèle sur le monde de la mémoire est les fichiers mappés en mémoire, ce scénario est idéal pour les gros morceaux de données (comme les gros fichiers). Ceci est utilisé en interne lorsque vous ouvrez un EXE (il ne charge pas l'EXE en mémoire, crée simplement un fichier mappé en mémoire).

2
No hay Problema