Allocation de mémoire

Un article de Wikipédia, l'encyclopédie libre.
Aller à : navigation, rechercher

Les algorithmes sous-jacents à tout programme informatique consomment essentiellement deux ressources : du temps et de l'espace. En machine, l'espace peut être la mémoire vive volatile ou la mémoire de masse persistante. Cet article discute de l'allocation de mémoire vive.

Trois stratégies d'allocation peuvent être employées : allocation statique, allocation dynamique sur la pile, et allocation dynamique sur le tas. L'allocation statique est principalement mise en place dans les langages de programmation compilés (C, C++, Pascal…). Les langages interprétés (Python, Perl…) ne peuvent allouer la mémoire que sur demande, lors de l'exécution du script.

À chacune des stratégies correspond une région mémoire du programme, ou segment. Ces segments sont nommés text (statique), stack (pile) et heap (tas).

L'opération symétrique de l'allocation est couramment appelée libération de la mémoire (on peut parler également de désallocation ou de restitution).

Les trois modes d'allocation[modifier | modifier le code]

Allocation statique[modifier | modifier le code]

L'allocation statique se fait avant l'exécution du programme, c'est-à-dire au moment de la création du programme, ce qui signifie que l'espace alloué statiquement est déjà réservé dans le fichier exécutable du programme lorsque le système d'exploitation charge le programme en mémoire pour l'exécuter.

Allouer statiquement de la mémoire pour un programme signifie :

  • prévoir l'espace mémoire nécessaire avant l'exécution du programme, en spécifiant la quantité nécessaire dans le code source ;
  • réserver cet espace au moment de la compilation, dans le fichier binaire produit ;
  • au chargement du programme en mémoire, juste avant l'exécution, l'espace réservé devient alors accessible.

Lors de l'exécution, aucune allocation n'a lieu.

L'avantage de l'allocation statique se situe essentiellement au niveau des performances, puisqu'on évite les coûts de l'allocation dynamique à l'exécution : la mémoire statique est immédiatement utilisable.

Sur les systèmes d'exploitation communs, la mémoire allouée statiquement est placée dans le segment text, le segment de données ou le segment BSS du programme, selon le destin de cette zone mémoire (réservée, initialisée, lecture seule…).

Allocation dynamique[modifier | modifier le code]

L'allocation dynamique se fait pendant l'exécution du programme, ce qui signifie que l'espace alloué dynamiquement ne se trouve pas déjà dans le fichier exécutable du programme lorsque le système d'exploitation charge le programme en mémoire pour l'exécuter; la demande d'allocation d'espace au système d'exploitation est durant l'exécution du programme.

Allocation sur la pile (automatique)[modifier | modifier le code]

L'exécution d'un programme utilise généralement une pile contenant les cadres d'appel aux routines (fonctions ou procédures) du langage de programmation utilisé. Schématiquement, les variables lexicales, c'est-à-dire les variables définies dans la portée textuelle d'une routine, sont :

  • allouées lors de l'entrée dans la routine, c'est-à-dire que l'espace est réservé pour ces variables;
  • désallouées automatiquement lors de la sortie (du retour) de la routine, c'est-à-dire que l'espace réservé pour ces variables est dorénavant libre et disponible pour d'autres variables.

Un segment mémoire, dit segment de pile, est utilisé pour ces allocations/désallocations. Aucun mot clef n'est nécessaire dans le code source du langage supportant la notion de variable lexicale : l'espace est alloué et libéré selon la discipline de pile par définition.

Certains langages, comme le C ou le C++, parlent de variables automatiques au lieu de variables lexicales, mais il s'agit de la même chose.

Allocation sur le tas[modifier | modifier le code]

La plupart des programmes ayant des besoins en mémoire dépendant de l'usage qu'on en fait, il est nécessaire de pouvoir, à des moments arbitraires de l'exécution, demander au système l'allocation de nouvelles zones de mémoire, et de pouvoir restituer au système ces zones (libérer la mémoire). Dans ce cas, l'allocation et la libération de la mémoire sont sous la responsabilité du programmeur. Les fuites de mémoire, ainsi que d'autres erreurs fréquentes dans les programmes à gestion manuelle de la mémoire, ont leur source dans les erreurs d'allocation mémoire sur le tas.

Classiquement, les fonctions de la bibliothèque standard de C malloc et free, les opérateurs du langage C++ new et delete permettent, respectivement, d'allouer et de libérer la mémoire sur le tas.

Les langages de programmation dotés de ramasse-miettes utilisent, mais de façon transparente pour le programmeur, l'allocation sur le tas et les primitives alloc/free. Dans ce dernier cas, le ramasse-miettes permet au développeur de ne pas se soucier de la libération des ressources.

Comparaison[modifier | modifier le code]

L'allocation statique est la méthode la plus sûre dans le sens où :

  • la quantité consommée est constante et complètement connue avant l'exécution ;
  • elle est généralement accessible exclusivement en lecture seule (non modifiable).

C'est toutefois une méthode très inflexible et insuffisante pour les programmes dont les besoins peuvent varier de façon imprévisible : pour un programme ayant des besoins potentiels de mémoire importants, l'allocation statique conduirait à un gaspillage. Elle est finalement essentiellement utilisée pour stocker des constantes ou des valeurs calculées et disponibles au moment de la compilation.

L'allocation sur la pile représente un bon compromis :

  • la quantité consommée est proportionnelle aux paramètres d'entrées du programme, qui déterminent la profondeur de la pile et donc la quantité allouée ; elle peut être connue avant l'exécution par une analyse de la complexité algorithmique ;
  • il n'y a pas de fuite de mémoire, du fait de la libération implicite (respectant la discipline de pile).

L'allocation sur la pile doit être choisie en priorité lorsque les besoins mémoires sont connus à l'avance et sont proportionnels à certains paramètres d'entrée du programme. L'allocation et la libération consécutives, selon une discipline de pile, de grosses quantités de mémoire peuvent toutefois poser des problèmes de performance et de lisibilité du source (explosion du nombre de paramètres passés aux fonctions) ; ces problèmes sont à mettre dans la balance contre l'assurance d'une libération correcte des ressources mémoire.

L'allocation sur le tas, puisqu'elle permet le contrôle complètement arbitraire de l'allocation et de la libération, offre le plus de possibilités. Les ressources allouées manuellement ont cependant une durée de vie indéfinie, c’est-à-dire que le programmeur a la responsabilité de la libération (et doit éviter de tomber dans des pièges comme par exemple la double libération, c'est-à-dire libérer deux fois la même zone mémoire). La mémoire allouée sur le tas peut également être référencée par des variables globales (de préférence confinées dans un espace de noms), ce qui peut aider à délester un groupe de fonctions de paramètres communs.

En pratique, on préfèrera le mode d'allocation le plus simple. La plupart du temps, l'allocation sur la pile est suffisante. On n'utilise l'allocation sur le tas qu'en dernier recours, pour manipuler des structures de données complexes et/ou dont l'évolution ne suit pas la discipline de pile.

Dans les langages à libération automatique de la mémoire (algorithme du ramasse-miettes) comme LISP ou Java, le choix du mode d'allocation est réalisé directement par le compilateur.

Voir aussi[modifier | modifier le code]