Skip to content

2 4 6 gestion memoire

Gestion de la mémoire à l'exécution

Noms et objets (rappel)

En Python, une variable est une référence à un objet. Manipuler cet objet se fait grâce à cette variable qui en quelque sorte l'identifie.

Ainsi :

 a = 3
 a = a + 1

crée un objet de classe entière (int) dont la valeur est 3, et place dans la variable a son adresse (sa référence). Ensuite l'addition crée un autre objet de type (classe) int et de valeur 4 et a reçoit la référence à ce nouvel objet.

  >>> dir(a) 
 ['__abs__', '__add__', '__and__', '__bool__', '__ceil__', '__class__',
  '__delattr__', '__divmod__', '__doc__', '__eq__', '__float__',
  '__floor__', '__floordiv__', '__format__', '__ge__',
  '__getattribute__', '__getnewargs__', '__gt__', '__hash__',
  '__index__', '__init__', '__int__', '__invert__', '__le__',
  '__lshift__', '__lt__', '__mod__', '__mul__', '__ne__', '__neg__',
  '__new__', '__or__', '__pos__', '__pow__', '__radd__', '__rand__',
  '__rdivmod__', '__reduce__', '__reduce_ex__', '__repr__',
  '__rfloordiv__', '__rlshift__', '__rmod__', '__rmul__', '__ror__',
  '__round__', '__rpow__', '__rrshift__', '__rshift__', '__rsub__',
  '__rtruediv__', '__rxor__', '__setattr__', '__sizeof__', '__str__',
  '__sub__', '__subclasshook__', '__truediv__', '__trunc__',
  '__xor__', 'bit_length', 'conjugate', 'denominator', 'from_bytes',
  'imag', 'numerator', 'real', 'to_bytes']

dir(a) renvoie la liste des attributs et méthodes de l'objet référencé par la variable a.

En fait en Python tout est objet (ou référence à un objet).

Dans le vocabulaire Python, une variable est appelé nom et est liée à un objet. Un objet peut avoir plusieurs noms, par exemple dans le code ci-dessous où l'objet de type liste est connu sous les noms s et t dans un code appelant et x dans la fonction foo appelée.

  def foo(x):
    x[0] = 3

  s = [0, 0, 7]
  t = s
  foo(s)

Tout objet Python x a un identificateur (donné par id(x)), un type (donné par type(x)), et un contenu.

Lors de son exécution, un programme Python gère des espaces de noms (namespace). Un espace de nom est un mappage (mapping) de noms vers des objets. Des exemples de tels espaces de noms sont donnés par l'ensemble des noms globaux (donné par globals()) ou des noms locaux à une instance de fonction (donné par locals()).

  • Les espaces de nom peuvent être implémentés sous forme de dictionnaires Python.

  • Ainsi, l'assignation ainsi que le passage de paramètres à une fonction modifient les espaces de noms et non les objets eux-mêmes !

  • Par contre :

  x = []
  x.append(3)

crée une liste et un nom dans l'espace de nom courant et ensuite appelle la méthode append() qui va modifier l'objet référencé par x.

Scope et espace de nom (namespace)

En pratique la gestion des objets et des espaces de nom en mémoire implique deux espaces mémoire :

1- l'un, appelé pile d'exécution (runtime stack) qui va contenir l'espace de nom global et les espaces de noms locaux ;

2- l'autre, tas d'exécution (runtime heap), qui contient les objets.

La gestion des espaces de nom locaux (ainsi que d'autres éléments en mémoire, nécessaires pour le bon fonctionnement du programme) est effectuée dans des trames (frames) dans la pile d'exécution (runtime stack) : chaque fois qu'une instance de fonction foo() est appelée, une trame associée à cette instance est créée et mise comme un élément supplémentaire de la pile d'exécution. Cette trame contient en particulier l'espace de nom local à cette instance. Cette trame sur la pile d'exécution continuera à exister jusqu'à la fin de l'exécution de cette instance de foo(). Ensuite (au moment du return), la trame est enlevée de la pile système et on revient à l'instance appelante dont la trame se trouve maintenant au sommet de la pile d'exécution.

Par contre un objet créé continue à exister jusqu'à ce qu'il ne soit plus relié à aucun nom dans un espace de noms quelconque et que le système décide de récupérer l'espace mémoire qui lui était attribué. Cette opération est nommée garbage collection et s'effectue de façon transparente pour le programmeur, si ce n'est qu'il peut observer un ralentissement ponctuel du programme pendant que le garbage collector s'exécute. C'est en raison de la gestion sans ordre de cet espace mémoire où se trouvent les objets Python, qu'il est appelé le tas d'exécution (runtime heap).

Exemple d'exécution d'un programme

Prenons l'exécution du programme suivant qui est une version simplifiée du tri par fusion pour des listes de caractères, exécutée sur la liste list("CDAB").

 def merge_sort(t): 
     """Tri par fusion de t."""
     if len(t) > 1:
        (t1, t2) = (t[:len(t)//2], t[len(t)//2:])
        t1 = merge_sort(t1) 
        t2 = merge_sort(t2) 
        # merge
        res = [] 
        while len(t1) > 0 and len(t2) > 0:
           if t1[0] < t2[0]: 
              res.append(t1[0])
              del t1[0]
           else: 
              res.append(t2[0])
              del t2[0]
        res += t1 + t2
     else: 
        res =  t
     return res

 liste = list("DCBA")
 print(merge_sort(liste))

Illustrons en particulier les valeurs des variables t1 et t2 avant et après les appels récursifs et au moment des return (pour simplifier les diagrammes d'états, on représente par le caractère "X" la référence à ce caractère "X").

État dans l'instance de fonction de niveau 1 ("DCBA") avant les appels au niveau 2.

État dans l'instance de fonction de niveau 2 ("DC") avant les appels au niveau 3.

État dans l'instance de fonction de niveau 3 pour ("D").

État dans l'instance de fonction de niveau 3 pour ("C").

État dans l'instance de fonction de niveau 2 après la fusion de "D" et "C".

Modification de t1 dans l'instance de fonction de niveau 1.

État dans l'instance de fonction de niveau 2 ("BA") avant les appels au niveau 3.

État dans l'instance de fonction de niveau 3 pour ("B").

État dans l'instance de fonction de niveau 3 pour ("A").

État dans l'instance de fonction de niveau 2 après la fusion de "B" et "A".

Modification de t2 dans l'instance de fonction de niveau 1.

Après fusion de "CD" avec "AB" dans l'instance de niveau 1 de merge_sort().