Pourquoi votre application a plus besoin du tas que de la pile

3

Votre ordinateur ne dispose pas seulement de RAM. Il présente un paysage de mémoire désordonné, fragmenté et en constante évolution que le processeur prétend être infini. C’est la réalité des structures de données dynamiques fonctionnant sur du matériel moderne.

Aujourd’hui, une station de travail typique repose sur 16 à 64 Mo de RAM physique. Mais ne vous laissez pas tromper par ces petits chiffres. Grâce à une technique appelée mémoire virtuelle, le système échange des données entre cette RAM et le disque dur. Le CPU voit une illusion. Il pense disposer de 200 à 500 mégaoctets d’espace contigu. L’illusion fonctionne. Le code s’exécute. Mais lorsque l’OS doit fouiller dans le disque dur pour récupérer une page de mémoire, tout ralentit. Vous le ressentez avec le décalage. Vous le ressentez dans la rotation du ventilateur. Malgré la baisse des performances, la mémoire virtuelle est un moyen peu coûteux d’« étendre » votre RAM. C’est un compromis nécessaire.

Supposons une table rase. Un espace mémoire total de 50 Mo. Pas plus. Rien de moins. C’est le budget.

Le système d’exploitation possède ce bloc de 50 mégaoctets. Cela divise le gâteau. Pas également. Jamais de la même manière.

Il y a d’abord le code. Les instructions exécutables pour chaque application en cours d’exécution, ainsi que le noyau du système d’exploitation lui-même. Cette section est statique. Cela ne change pas tant que le programme est en vie. Viennent ensuite les variables globales. Ce sont les constantes partagées et les indicateurs d’état que chaque partie de l’application peut toucher. Ils restent en mémoire depuis le lancement de l’application jusqu’à sa mort.

Ensuite, il y a la pile.

La pile est rigide. Il grandit et diminue au rythme des appels de fonction. Lorsque vous invoquez une fonction, ses variables et paramètres locaux sont placés sur la pile. Lorsque la fonction revient, ils sont supprimés. La pile se souvient de l’ordre d’appel. Cela garantit que vous revenez au bon endroit. Mais il a une taille maximale. Vous ne pouvez pas demander plus de mémoire à la pile en cours de vol. Si vous essayez, la pile déborde. Le programme plante.

Lorsqu’un programme se termine, le système d’exploitation le décharge. Le code, les variables globales, l’espace de la pile : tout est effacé. Cette mémoire est recyclée. C’est prêt pour le prochain programme.

Mais voici le problème. À tout moment, environ 50 % de cet espace de 50 mégaoctets peut être inutilisé. Pourquoi? Parce que la pile ne contient que ce qui est en cours d’exécution. Le code ne contient que les instructions. Les morceaux de mémoire inutilisés sont dispersés. Ce sont des trous.

Le système d’exploitation prend ces trous et les regroupe. Il appelle cette piscine collective le tas.

C’est là que se produit l’allocation dynamique. Le tas est le seul endroit de la mémoire où un programme peut demander une quantité exacte d’espace, précisément lorsqu’il en a besoin. Vous ne déclarez pas un tableau de 1 000 entiers au moment de la compilation. Vous ne savez pas si vous en aurez besoin de 1 000 ou de 100 000. Vous attendez. Vous exécutez le code. Vous décidez que vous avez besoin de cet espace. Ensuite, vous appelez malloc.

malloc signifie allocation de mémoire. Il récupère un bloc du tas. Il renvoie un pointeur. Vous utilisez ce pointeur. Lorsque vous avez terminé, vous appelez « gratuitement ». Le bloc retourne dans la piscine.

C’est la différence fondamentale entre la mémoire statique et dynamique. La pile est automatique. C’est lié à la portée. Le tas est manuel. C’est lié à l’intention.

Pourquoi est-ce important pour vous ? Parce que la plupart des bugs dans les logiciels complexes ne proviennent pas d’une mauvaise logique. Ils proviennent d’une mauvaise gestion de la mémoire. Si vous allouez à partir du tas et oubliez de le libérer, vous perdez de la mémoire. Le tas rétrécit. Finalement, le système manque d’espace. Les programmes se figent. Le système d’exploitation tue les processus. Vous perdez le travail non sauvegardé.

À l’inverse, si vous essayez d’en allouer trop sur la pile, vous dépassez la limite de pile. Le programme se termine brusquement.

Le tas permet une certaine flexibilité. Cela permet des structures de données qui se développent. Listes liées. Des arbres. Graphiques. Aucun de ceux-ci ne peut être défini au moment de la compilation avec une taille fixe. Ils doivent être construits à la volée. Ils doivent vivre dans le tas.

Le tas est-il parfait ? Non, ça se fragmente. Au fur et à mesure que vous allouez et libérez des blocs de différentes tailles, le tas devient un puzzle avec des pièces manquantes. La recherche d’un bloc contigu de mémoire libre peut devenir lente. C’est pourquoi « malloc » peut parfois prendre un temps considérable. Il faut chercher. Il faut que cela fusionne. Il lui faut gérer le chaos.

Mais sans le tas, l’informatique moderne s’effondre.