Et si Malloc échoue?

Si une allocation de malloc échoue, devrions-nous essayer à nouveau?

Dans quelque chose comme ça:

 char* mystrdup(const char *s) { char *ab = NULL; while(ab == NULL) { ab=(char*)malloc(strlen(s)+1); } strcpy(ab, s); return ab; } 

La boucle while est-elle valide pour vérifier l’allocation de mémoire?

En général, une implémentation moderne de malloc() retournera NULL uniquement en dernier recours, et réessayer n’aura aucun effet. La seule chose qui vous aidera à libérer de la mémoire puis à réessayer. Si votre application contient des ressources consomptibles, ce sera le moment de les libérer, puis de tenter votre chance.

Dans certains environnements, une pratique utile consiste à allouer une petite quantité de mémoire sous la forme d’un fonds jour de pluie . Si malloc() ne renvoie jamais la NULL , vous pouvez libérer ce fonds Rainy-Day, puis allouer toutes les ressources nécessaires pour pouvoir gérer l’erreur et quitter en douceur. C’était une pratique courante lors de la programmation avec l’ancien Macintosh Toolbox; Si malloc() renvoyé la NULL , vous pouvez utiliser cet espace pour créer une boîte de dialog permettant de signaler le problème avant de quitter.

Dans un programme à un seul thread, “réessayer” sans libérer de mémoire entre les tentatives n’a aucun sens pratique. Ça va juste boucler pour toujours.

Dans un programme multi-thread, cela pourrait “fonctionner”, si un autre thread s’exécutant en parallèle décidait soudainement de libérer une partie de sa propre mémoire. Dans ce cas, la boucle constituerait une boucle classique “d’attente en attente”. Mais même dans ce cas, un tel code a très peu de valeur pratique pour plus d’une raison.

Non jamais. Si malloc renvoie NULL, cela indique une erreur et vous devriez probablement abandonner.

Sans chercher à savoir pourquoi ou quand cela serait utile, les tentatives de réaffectation dans une boucle pourraient fonctionner, du moins sous Windows avec un code 64 bits et les parameters de fichier d’échange par défaut. De plus, cela pourrait acheter étonnamment plus de mémoire virtuelle supplémentaire. Cependant, ne faites pas cela dans une boucle infinie, mais utilisez plutôt un nombre fini d’essais. Pour preuve, essayez le code suivant qui simule une fuite de 1 Mo de mémoire. Vous devez l’exécuter dans la version Release, de préférence pas sous le débogueur.

 for (int i = 0; i < 10; i++) { size_t allocated = 0; while (1) { void* p = malloc(1024 * 1024); if (!p) break; allocated += 1; } //This prints only after malloc had failed. std::cout << "Allocated: " << allocated << " Mb\n"; //Sleep(1000); } 

Sur ma machine avec 8 Go de RAM et un fichier d'échange géré par le système, j'obtiens la sortie suivante (construite avec VS2013 pour cible x64, testée sur Windows 7 Pro):

 Allocated: 14075 Mb Allocated: 16 Mb Allocated: 2392 Mb Allocated: 3 Mb Allocated: 2791 Mb Allocated: 16 Mb Allocated: 3172 Mb Allocated: 16 Mb Allocated: 3651 Mb Allocated: 15 Mb 

Je ne connais pas la raison exacte d'un tel comportement, mais il semble que les allocations commencent à échouer une fois que le redimensionnement du fichier d'échange ne peut plus suivre le rythme des demandes. Sur ma machine, le fichier d'échange est passé de 8 Go à 20 Go après cette boucle (retour à 8 Go après la fermeture du programme).

Il est incroyablement improbable que cela fasse ce que vous voulez. Si vous manquez de mémoire, il sera probablement décevant de boucler la boucle jusqu’à ce que vous en obteniez plus. Vous devez simplement renvoyer la valeur NULL au programme appelant afin qu’il puisse gérer l’épuisement des ressources, soit en libérant la mémoire dont il n’a plus besoin, soit en renvoyant une erreur.

malloc () fait de son mieux pour allouer de la mémoire. Si cela échoue, au lieu d’essayer à nouveau d’allouer de la mémoire dans une boucle while (le programme risque de restr bloqué pour toujours), essayez de libérer de la mémoire détenue par un autre processus ou thread, si vous le pouvez, puis réessayez.

Une autre alternative consisterait à augmenter la mémoire, en augmentant le nombre de fichiers d’échange ou la mémoire de pagination, à la volée, à partir du code lui-même (mais dangereux et non préférable) ou en le faisant manuellement.

Le meilleur moyen d’éviter de tels problèmes consiste à calculer ou à estimer les besoins en mémoire, tout en écrivant le code lui-même.

Essayez d’augmenter la taille du tas (mémoire réservée à l’allocation dynamic).