Comment gérer realloc quand il échoue à cause de la mémoire?

La question dit tout mais voici un exemple:

typedef struct mutable_t{ int count, max; void **data; } mutable_t; void pushMutable(mutable_t *m, void *object) { if(m->count == m->max){ m->max *= 2; m->data = realloc(m->data, m->max * sizeof(void*)); } // how to handle oom?? m->data[m->count++] = object; } 

Comment puis-je gérer le manque de mémoire et non NULL sur toutes mes données?

edit – supposons qu’il y ait quelque chose à faire, par exemple libérer de la mémoire quelque part ou au moins dire à l’utilisateur “tu ne peux pas faire ça – tu n’as plus de mémoire”. Idéalement, j’aimerais laisser ce qui a été alloué là-bas.

La technique standard consiste à introduire une nouvelle variable pour conserver le retour de realloc. Vous écrasez alors votre variable d’entrée uniquement si elle réussit:

 tmp = realloc(orig, newsize); if (tmp == NULL) { // could not realloc, but orig still valid } else { orig = tmp; } 

La stratégie à suivre en cas d’échec de realloc() dépend de votre application. La question est trop générique pour pouvoir répondre à tous les cas possibles.

Quelques autres notes:

Jamais fait:

 a = realloc(a, size); 

Si realloc() échoue, vous perdez le pointeur d’origine et realloc() ne free() pas free() la mémoire d’origine. Vous obtiendrez ainsi une fuite de mémoire. Au lieu de cela, faites:

 tmp = realloc(a, size); if (tmp) a = tmp; else /* handle error */ 

Le deuxième point que je veux dire est mineur et peut ne pas être aussi critique, mais il est bon de le savoir de toute façon: augmenter la mémoire à allouer par un facteur f est bon. Disons que vous malloc() n octets en premier. Ensuite, vous avez besoin de plus de mémoire, vous devez donc realloc() de taille n × f . Ensuite, vous avez besoin de plus de mémoire, vous avez donc besoin de n × f 2 octets. Si vous voulez que realloc() utilise l’espace des deux blocs de mémoire précédents, assurez-vous que n × f 2 ≤ n + n × f . En résolvant cette équation, nous obtenons f≤ (sqrt (5) +1) / 2 = 1.618 (le nombre d’ or ). J’utilise un facteur de 1.5 plupart du temps.

C’est un sujet brûlant, car il existe essentiellement deux écoles de pensée sur le sujet.

  1. Détectez le MOO et demandez à la fonction de renvoyer un code d’erreur.
  2. Détecter le MOO et bloquer votre processus le plus rapidement possible

Personnellement je suis au camp n ° 2. Attendez-vous à des types très spéciaux d’applications, le MOO est une période fatale. Certes, un code parfaitement écrit peut gérer un MOO, mais si peu de gens comprennent comment écrire du code sans risque en mémoire. Encore moins de gens se donnent la peine de le faire car cela ne vaut presque jamais la peine.

Je n’aime pas transmettre le code d’erreur à la fonction appelante pour les MOO car cela revient à dire à l’appelant “J’ai échoué et rien ne peut être résolu”. Au lieu de cela, je préfère planter rapidement afin que le vidage résultant soit le plus instructif possible.

La première règle que vous devez suivre lorsque vous travaillez avec realloc consiste à ne pas affecter la valeur de retour de realloc au même pointeur que celui que vous lui avez transmis. Ce

 m->data = realloc(m->data, m->max * sizeof(void*)); 

est mauvais. Si realloc échoue, il renvoie le pointeur null, mais il ne libère pas l’ancienne mémoire. Le code ci-dessus annulera vos m->data tandis que l’ancien bloc de mémoire précédemment désigné par m->data deviendra probablement une fuite de mémoire (si vous n’avez aucune autre référence).

La valeur de retour de realloc doit être stockée dans un pointeur séparé en premier

 void **new_data; ... new_data = realloc(m->data, m->max * sizeof(void*)); 

Ensuite, vous pouvez vérifier le succès / l’échec et changer la valeur de m->data en cas de succès

 if (new_data != NULL) m->data = new_data; else /* whatever */; 

C’est tout à fait votre problème! Voici quelques critères:

  • Vous avez demandé cette mémoire pour une raison. S’il n’est pas disponible, le travail de votre programme est-il condamné ou peut-il continuer à faire des choses? Si c’est le cas, vous voulez terminer votre programme avec un message d’erreur; sinon, vous pouvez afficher un message d’erreur et continuer.

  • Est-il possible d’échanger du temps contre de l’espace? Pourriez-vous répondre à l’opération que vous avez tentée avec un algorithme utilisant moins de mémoire? Cela ressemble à beaucoup de travail, mais serait en fait une possibilité de continuer le fonctionnement de votre programme malgré le manque de mémoire au départ.

  • Votre programme aurait-il tort de continuer à boiter sans ces données et sans assez de mémoire? Si c’est le cas, vous devriez terminer avec un message d’erreur. Il vaut bien mieux tuer votre programme que de continuer aveuglément à traiter des données incorrectes.

  1. Découvrez comment l’infrastructure d’application gère un MOO. Beaucoup ne veulent tout simplement pas gérer un MOO. La plupart du temps, un cadre ne fonctionnera pas correctement dans des conditions de mémoire non libre, à moins d’indiquer très clairement et sans ambiguïté qu’il le fera. Si le framework ne gère pas un MOO et qu’il est multithread (beaucoup le sont de nos jours), un MOO sera la fin de la série dans de nombreux cas. Même s’il n’est pas multithread, il risque de s’effondrer. Que vous quittiez le processus ou que le cadre le fasse peut être un point discutable; une sortie immédiate prévisible peut simplement être un peu mieux qu’un crash dans un proche avenir dans un état semi-aléatoire.

  2. Si vous utilisez un pool distinct de sous-mémoires à usage spécifique (c’est-à-dire pas votre malloc habituel) pour un ensemble bien défini d’opérations qui ne sont contraintes que par une utilisation en mémoire par le MOO (c’est-à-dire que l’opération en cours est annulée ou annulée proprement MOO pour le pool de sous-mémoire, et non pas pour le processus entier ou le pool de mémoire principale), et ce sous-pool n’est pas également utilisé par le cadre d’application, ou si votre cadre et le TOUT DU rest de l’application sont conçus pour conserver une signification l’état et la poursuite du fonctionnement dans des conditions de mémoire non-libre (rare mais pas inconnue en mode kernel et certains types de programmation système), vous avez peut-être raison de renvoyer un code d’erreur plutôt que de bloquer le processus.

  3. Idéalement, la majeure partie des allocations de mémoire (ou même plus idéalement de toutes les allocations) pour un traitement doit être allouée dès que possible dans le traitement, idéalement avant de commencer correctement, afin de minimiser les problèmes de perte d’intégrité des données et / ou de quantité de données. recoder le code requirejs si elle échoue. Dans la pratique, pour économiser les coûts de programmation et les coûts de programmation sur les projets, pour préserver l’intégrité des données, les applications reposent sur des transactions de firebase database et obligent l’utilisateur / le support à détecter un plantage de l’interface graphique (ou du serveur) et à redémarrer l’application lorsqu’elle est épuisée. Des erreurs de mémoire se produisent, plutôt que d’être écrites pour gérer et annuler des milliers de situations potentielles de MOO de la meilleure façon possible. Les efforts se concentrent ensuite sur l’essai de limiter l’exposition de l’application aux situations de surcharge, ce qui peut inclure une validation supplémentaire et des limites relatives à la taille des données, ainsi qu’aux connexions et requêtes simultanées.

  4. Même si vous vérifiez la quantité de mémoire déclarée comme disponible, un autre code peut souvent allouer ou libérer de la mémoire comme vous le faites, modifiant ainsi la base de votre vérification de la mémoire et pouvant éventuellement conduire au MOO. Par conséquent, vérifier la RAM disponible disponible avant d’allouer ne constitue souvent pas une solution fiable au problème de vérifier que votre application fonctionne dans les limites de RAM disponibles et maintient l’intégrité des données suffisamment longtemps pour satisfaire les utilisateurs.

  5. La meilleure situation est de savoir combien de mémoire votre application nécessite dans tous les cas possibles, y compris les frais généraux du framework, et de conserver ce chiffre dans la quantité de RAM disponible pour votre application, mais les systèmes sont souvent si compliqués avec des dépendances externes dictant La taille des données peut donc être irréaliste.

Le test décisif est bien sûr de satisfaire les utilisateurs de manière suffisante par des temps de disponibilité élevés et une corruption, une perte ou des plantages de données rares. Dans certains cas, une application ayant un processus de surveillance pour la redémarrer si elle se bloque est utile.

En ce qui concerne realloc:

Vérifiez la valeur de retour de realloc – mettez-la dans une variable temporaire. Faites seulement attention si c’est NULL si la nouvelle taille demandée était> 0. Dans d’autres cas, placez-le dans votre variable non temporaire:

par exemple

  void* temp = realloc(m->data, m->max * sizeof(void*)); if (m->max!=0&&temp==NULL) { /* crash or return error */ } m->data =(void**)temp; 

MODIFIER

Remplacé “la plupart des cas” par “beaucoup de cas” dans (1).

Je reconnais que vous avez dit que “quelque chose peut être fait” si la mémoire ne peut pas être allouée. Mais la gestion de la mémoire est une considération très globale (!).

Il existe également une autre erreur subtile qui peut provenir de realloc. La fuite de mémoire provenant du pointeur NULL renvoyé est plutôt bien connue (mais il est assez rare de tomber sur elle). J’ai eu dans mon programme un crash de temps en temps provenant d’un appel de realloc. J’avais une structure dynamic qui ajustait automatiquement sa taille avec un realloc semblable à celui-ci:

 m->data = realloc(m->data, m->max * sizeof(void*)); 

L’erreur que j’ai faite était de ne pas vérifier si m-> max == 0, ce qui libérait la zone mémoire. Et fait de mon m-> pointeur de données un fade.

Je sais que c’est un peu hors sujet, mais c’est le seul vrai problème que j’ai jamais rencontré avec realloc.

J’ai rencontré le problème. La configuration est OS: win7 (64); IDE: vs2013; Debug (Win32).
Lorsque mon realloc a renvoyé la valeur null en raison de la mémoire in.J’ai deux solutions:

1.Changez la propriété du projet pour activer les grandes adresses.
2.Changez ma plate-forme de solution de Win32 à x64.