Quelles sont les stratégies de gestion de mémoire insuffisante dans la programmation en C?

Une stratégie que je pensais à moi-même consiste à allouer 5 Mo de mémoire (ou le nombre que vous jugez nécessaire) au démarrage du programme.

Puis, à tout moment, lorsque malloc() renvoie la NULL , vous libérez les 5 mégaoctets et appelez à nouveau malloc() , ce qui aboutira et laissera le programme s’exécuter.

Que pensez-vous de cette stratégie?

Et quelles autres stratégies connaissez-vous?

Merci, Boda Cydo.

Gérez les échecs de malloc en quittant avec élégance. Avec les systèmes d’exploitation, les fichiers d’échange, etc. modernes, vous ne devez jamais vous préparer de façon préventive en cas de défaillance de la mémoire, mais simplement quitter correctement. Il est peu probable que vous renconsortingez des erreurs de mémoire insuffisante, sauf en cas de problème d’algorithme.

En outre, allouer 5MB sans raison au démarrage est insensé.

Au cours des dernières années, le logiciel (intégré) avec lequel j’ai travaillé ne permet généralement pas l’utilisation de malloc (). La seule exception à cette règle est que cela est autorisé pendant la phase d’initialisation, mais une fois qu’il est décidé qu’aucune allocation de mémoire n’est autorisée, tous les futurs appels à malloc () échouent. Comme la mémoire peut devenir fragmentée en raison de malloc () / free (), il devient au mieux difficile dans de nombreux cas de prouver que les prochains appels à malloc () n’échoueront pas.

Un tel scénario pourrait ne pas s’appliquer à votre cas. Cependant, savoir pourquoi malloc () échoue peut être utile. La technique suivante que nous utilisons dans notre code, étant donné que malloc () n’est généralement pas disponible, pourrait (ou non) être applicable à votre scénario.

Nous avons tendance à compter sur des pools de mémoire. La mémoire de chaque pool est allouée pendant la phase de démarrage transitoire. Une fois que nous avons les piscines, nous obtenons une entrée de la piscine lorsque nous en avons besoin et la rendons à la piscine lorsque nous avons terminé. Chaque pool est configurable et est généralement réservé à un type d’object particulier. Nous pouvons suivre l’utilisation de chacun au fil du temps. Si nous manquons d’entrées dans le pool, nous pouvons savoir pourquoi. Si nous ne le faisons pas, nous avons la possibilité de réduire la taille de notre pool et d’économiser des ressources.

J’espère que cela t’aides.

En tant que méthode de test que vous gérez avec soin dans les situations de mémoire insuffisante, cette technique peut s’avérer relativement utile.

Dans toute autre circonstance, cela semble au mieux inutile. Vous faites en sorte que la situation manque de mémoire, puis vous résolvez le problème en libérant de la mémoire dont vous n’aviez pas besoin au départ.

“Réessayez plus tard”. Ce n’est pas parce que vous êtes OOM que vous le serez plus tard, lorsque le système sera moins occupé.

 void *smalloc(size_t size) { for(int i = 0; i < 100; i++) { void *p = malloc(size); if(p) return p; sleep(1); } return NULL; } 

Vous devriez bien sûr penser beaucoup à l’endroit où vous employez une telle stratégie, car elle est assez cachée, mais cela a sauvé certains de nos systèmes dans divers cas.

Cela dépend en fait d’une stratégie que vous souhaitez implémenter, c’est-à-dire quel est le comportement attendu de votre programme lorsqu’il manque de mémoire.

Une bonne solution serait d’allouer de la mémoire lors de l’initialisation uniquement et jamais lors de l’exécution. Dans ce cas, vous ne manquerez jamais de mémoire si le programme parvient à démarrer.

Une autre pourrait être la libération de ressources lorsque vous atteignez la limite de mémoire. Ce serait difficile à mettre en œuvre et à tester.

N’oubliez pas que lorsque vous obtenez NULL auprès de malloc cela signifie que la mémoire physique et virtuelle n’a plus d’espace libre, ce qui signifie que votre programme échange tout le temps, ce qui le ralentit et l’ordinateur ne répond plus.

Vous devez en fait vous assurer (par calcul estimatif ou en vérifiant la quantité de mémoire en cours d’exécution) que la quantité de mémoire disponible attendue de l’ordinateur est suffisante pour votre programme.

En règle générale, le but de la libération de la mémoire est de vous permettre de signaler l’erreur avant de mettre fin au programme.

Si vous voulez continuer à courir, il est inutile de préallouer la réserve d’urgence.

Oui, cela ne fonctionne pas dans la pratique. D’abord pour des raisons techniques, une implémentation typique de segment de mémoire à faible fragmentation ne permet pas de disposer de grands blocs libres pour de petites allocations.

Mais le vrai problème est que vous ne savez pas pourquoi vous avez manqué d’espace mémoire virtuel. Et si vous ne savez pas pourquoi, vous ne pouvez rien faire pour éviter que cette mémoire supplémentaire ne soit utilisée très rapidement et pour bloquer votre programme avec le MOO. Ce qui est très susceptible de se produire, vous avez déjà consommé près de deux gigaoctets, ces 5 Mo supplémentaires représentent une goutte d’eau sur une plaque chauffante.

Tout type de système qui bascule l’application en «mode d’urgence» est très peu pratique. Vous devrez abandonner le code en cours pour pouvoir arrêter, par exemple, de charger un énorme fichier de données. Cela nécessite une exception. Maintenant, vous êtes revenu à ce que vous aviez déjà avant, std :: badalloc.

La plupart des systèmes d’exploitation modernes dans la configuration par défaut autorisent une surcharge de mémoire, afin que votre programme n’obtienne absolument pas la valeur NULL de malloc () ou du moins tant qu’il n’aura pas épuisé (par erreur, tout l’espace disponible (et non la mémoire). Et puis, il écrit un emplacement de mémoire parfaitement légal, obtient une erreur de page, il n’y a pas de page de mémoire dans le magasin de sauvegarde et BANG (SIGBUS) – vous êtes mort, et il n’y a pas de bonne solution.

Alors oubliez ça, vous ne pouvez pas y faire face.

Je souhaite également appuyer le sentiment selon lequel l’approche de pré-allocation de 5 Mo est “insensée”, mais pour une autre raison: elle est soumise aux conditions de concurrence. Si la cause de l’épuisement de la mémoire est dans votre programme (espace d’adressage virtuel épuisé), un autre thread pourrait réclamer le fichier de 5 Mo après l’avoir libéré, mais avant de pouvoir l’utiliser. Si l’épuisement de la mémoire est dû à un manque de ressources physiques sur la machine du fait que d’autres processus utilisent trop de mémoire, ces processus pourraient réclamer 5 Mo après l’avoir libéré (si l’implémentation malloc restitue l’espace sur le système).

Certaines applications, telles que les lecteurs de musique ou de films, seraient parfaitement justifiées en quittant / écrasant sur des échecs d’allocation – ils gèrent peu de données modifiables, voire aucune. D’autre part, je crois que toute application utilisée pour modifier des données potentiellement précieuses doit disposer d’un moyen permettant (1) de s’assurer que les données déjà présentes sur le disque restnt dans un état cohérent et non corrompu, et (2) d’écrire créer un journal de récupération afin que, lors des appels suivants, l’utilisateur puisse récupérer les données perdues lors de la fermeture forcée de l’application.

Comme nous l’avons vu dans le premier paragraphe, votre approche “malloc 5mb and free it” ne fonctionne pas. Dans l’idéal, le code permettant de synchroniser les données et d’écrire les informations de récupération ne nécessiterait aucune allocation; Si votre programme est bien conçu, il est probablement naturellement sans allocation. Une approche possible si vous savez que vous aurez besoin d’allocations à cette étape consiste à implémenter votre propre allocateur fonctionnant dans un petit pool / tampon statique, et à l’utiliser lors de la fermeture de l’allocation-échec.