75 votes

Le message "Out of Memory" est-il une erreur récupérable ?

Je programme depuis longtemps, et les programmes que je vois, lorsqu'ils manquent de mémoire, tentent de nettoyer et de sortir, c'est-à-dire d'échouer gracieusement. Je ne me souviens pas de la dernière fois où j'en ai vu un tenter de récupérer et de continuer à fonctionner normalement.

Une si grande partie du traitement dépend de la capacité à allouer la mémoire avec succès, en particulier dans les langages avec collecte des déchets, il semble que les erreurs de mémoire devraient être classées comme non récupérables. (Les erreurs non récupérables comprennent des choses comme les débordements de pile).

Quel est l'argument convaincant pour en faire une erreur récupérable ?

0 votes

Désolé si le commentaire ci-dessus était grossier. Mais, par exemple, les applications Java GUI gèrent assez bien l'erreur OutOfMemoryError (je ne suis pas un grand fan de Java, je ne fais que noter mon expérience) - il s'agit par exemple de la seule demande provenant d'une action unique de l'utilisateur.

0 votes

De même, il est parfois bon de laisser mourir un seul thread mais de laisser le reste du programme fonctionner - cela se produit parfois naturellement.

0 votes

De plus, la défaillance peut parfois être contenue dans le sabotage du traitement d'un seul événement (comme dans la boucle/thread de distribution d'événements d'une interface graphique).

37voto

Jon Skeet Points 692016

Cela dépend vraiment de ce que vous construisez.

Il n'est pas totalement déraisonnable pour un serveur web d'échouer une paire requête/réponse mais de continuer à répondre à d'autres requêtes. Il faudrait cependant s'assurer que l'échec unique n'a pas d'effets néfastes sur l'état global - c'est là que le bât blesse. Étant donné qu'une défaillance provoque une exception dans la plupart des environnements gérés (par exemple, .NET et Java), je pense que si l'exception est gérée dans le "code utilisateur", elle pourra être récupérée pour les futures demandes. Par exemple, si une demande tente d'allouer 10 Go de mémoire et échoue, cela ne devrait pas nuire au reste du système. Par contre, si le système manque de mémoire alors qu'il essaie de transférer la demande au code utilisateur, ce genre de chose pourrait être plus grave.

41 votes

Whoa... Jon Skeet répondant à une question de Walter Bright. Je viens d'avoir un frisson dans le dos (ou peut-être que c'est parce qu'il fait 20°C ici).

17voto

Aaron Digulla Points 143830

Dans une bibliothèque, vous souhaitez copier efficacement un fichier. Pour ce faire, vous constaterez généralement que la copie à l'aide d'un petit nombre de gros morceaux est beaucoup plus efficace que la copie d'un grand nombre de petits morceaux (par exemple, il est plus rapide de copier un fichier de 15 Mo en copiant 15 morceaux de 1 Mo que de copier 15'000 morceaux de 1K).

Mais le code fonctionne avec n'importe quelle taille de morceau. Donc, bien que cela puisse être plus rapide avec des morceaux de 1 Mo, si vous concevez un système où beaucoup de fichiers sont copiés, il peut être judicieux d'attraper OutOfMemoryError et de réduire la taille des morceaux jusqu'à ce que vous réussissiez.

Un autre endroit est un cache pour les objets stockés dans une base de données. Vous voulez garder autant d'objets que possible dans le cache, mais vous ne voulez pas interférer avec le reste de l'application. Étant donné que ces objets peuvent être recréés, une façon intelligente de conserver la mémoire est d'attacher le cache à un gestionnaire d'absence de mémoire pour supprimer les entrées jusqu'à ce que le reste de l'application ait suffisamment d'espace pour respirer à nouveau.

Enfin, pour la manipulation d'images, vous voulez charger autant d'images que possible en mémoire. Là encore, un gestionnaire d'OOM vous permet d'implémenter cela sans connaître à l'avance la quantité de mémoire que l'utilisateur ou le système d'exploitation accordera à votre code.

[EDIT] Notez que je pars du principe que vous avez alloué à l'application une quantité fixe de mémoire et que cette quantité est inférieure à la mémoire totale disponible, hors espace d'échange. Si vous pouvez allouer une telle quantité de mémoire qu'une partie de celle-ci doit être swapée, plusieurs de mes commentaires n'ont plus de sens.

7 votes

Sur un système avec de la mémoire virtuelle, vérifier combien vous pouvez allouer signifie que vous allez allouer de la mémoire qui est paginée sur le disque, ce qui est une pessimisation pour un tampon de disque.

1 votes

Point de données pour votre défense : la uClibc possède un tampon statique interne d'environ 8 octets pour les entrées/sorties de fichiers lorsqu'il n'y a plus de mémoire à allouer dynamiquement.

9voto

Les utilisateurs de MATLAB manquent constamment de mémoire lorsqu'ils effectuent des opérations arithmétiques sur de grands tableaux. Par exemple, si la variable x tient dans la mémoire et qu'ils exécutent "x+1", MATLAB alloue de l'espace pour le résultat et le remplit ensuite. Si l'allocation échoue, MATLAB se trompe et l'utilisateur peut essayer autre chose. Il serait catastrophique que MATLAB se ferme à chaque fois que ce cas d'utilisation se présente.

8voto

Ifeanyi Echeruo Points 468

L'OOM devrait être récupérable parce que l'arrêt n'est pas la seule stratégie de récupération de l'OOM.

Il existe en fait une solution assez standard au problème de l'OOM au niveau de l'application. Dans le cadre de la conception de votre application, déterminez la quantité minimale de mémoire nécessaire pour récupérer d'une condition d'absence de mémoire. (Par exemple, la mémoire requise pour sauvegarder automatiquement des documents, afficher des boîtes de dialogue d'avertissement, enregistrer des données d'arrêt).

Au début de votre application ou au début d'un bloc critique, préallouez cette quantité de mémoire. Si vous détectez une situation d'absence de mémoire, libérez votre mémoire de garde et effectuez la récupération. Cette stratégie peut toujours échouer mais, dans l'ensemble, elle est très rentable.

Notez que l'application ne doit pas nécessairement s'arrêter. Elle peut afficher une boîte de dialogue modale jusqu'à ce que la condition d'OOM ait été résolue.

Je ne suis pas sûr à 100%, mais je suis presque sûr Code complet (lecture obligatoire pour tout ingénieur logiciel respectable) couvre ce sujet.

P.S. Vous pouvez étendre votre cadre d'application pour vous aider dans cette stratégie, mais ne mettez pas en œuvre une telle politique dans une bibliothèque (les bonnes bibliothèques ne prennent pas de décisions globales sans le consentement des applications).

0 votes

Ceci est mentionné dans Code Complete. La technique s'appelle un "parachute" si je me souviens bien.

0 votes

Malheureusement, cela ne peut pas être fait de manière fiable en Java, car la JVM est autorisée à lancer des OOM à l'adresse suivante cualquier temps, pas seulement là où vous avez un new . Par conséquent, si vous attrapez un OOM, il pourrait ont été lancés à un moment qui a laissé votre programme dans un état incohérent. Voir stackoverflow.com/questions/8728866/

5voto

n-alexander Points 2685

Je travaille sur un système qui alloue de la mémoire pour le cache IO afin d'augmenter les performances. Puis, lorsqu'il détecte un OOM, il en reprend une partie, afin que la logique de l'entreprise puisse se poursuivre, même si cela signifie moins de cache IO et des performances d'écriture légèrement inférieures.

J'ai également travaillé avec une application Java intégrée qui tentait de gérer les OOM en forçant la collecte des déchets, en libérant éventuellement certains objets non critiques, comme les données prélevées ou mises en cache.

Les principaux problèmes liés au traitement des OOM sont les suivants :

1) être capable de réessayer à l'endroit où cela s'est produit ou être capable de revenir en arrière et de réessayer depuis un point plus élevé. La plupart des programmes contemporains s'appuient trop sur le langage pour lancer et ne gèrent pas vraiment l'endroit où ils se retrouvent et la manière de réessayer l'opération. En général, le contexte de l'opération est perdu, s'il n'a pas été conçu pour être préservé.

2) être capable de libérer de la mémoire. Cela signifie qu'il faut une sorte de gestionnaire de ressources qui sache quels objets sont critiques et lesquels ne le sont pas, et que le système soit capable de redemander les objets libérés lorsqu'ils deviennent critiques par la suite.

Une autre question importante est de pouvoir revenir en arrière sans déclencher une nouvelle situation d'erreur. Il s'agit d'un aspect difficile à maîtriser dans les langages de niveau supérieur.

En outre, le système d'exploitation sous-jacent doit se comporter de manière prévisible en ce qui concerne les OOM. Linux, par exemple, ne le fera pas si le surcommit de mémoire est activé. De nombreux systèmes compatibles avec l'échange mourront plus tôt que de signaler l'OOM à l'application incriminée.

Et, il y a le cas où ce n'est pas votre processus qui a créé la situation, alors libérer de la mémoire ne sert à rien si le processus fautif continue à fuir.

Pour toutes ces raisons, ce sont souvent les gros systèmes et les systèmes intégrés qui emploient ces techniques, car ils ont le contrôle du système d'exploitation et de la mémoire pour les permettre, et la discipline/motivation pour les mettre en œuvre.

0 votes

Vous considérez donc que la récupération d'un OOM se fait par une méthode d'allocation de mémoire personnalisée, et non par une méthode de la bibliothèque standard ?

0 votes

Vous pouvez également le faire avec la méthode standard, si vous savez quoi libérer et si vous pouvez le faire sans nécessiter de nouvelle mémoire. Nous l'avons fait avec le gestionnaire de mémoire standard de Java avec un certain succès.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X