Qu'est-ce que la fragmentation de la mémoire?

204

J'ai entendu le terme "fragmentation de la mémoire" utilisé plusieurs fois dans le contexte de l'allocation dynamique de mémoire C ++. J'ai trouvé quelques questions sur la façon de gérer la fragmentation de la mémoire, mais je ne trouve pas de question directe qui la traite elle-même. Alors:

  • Qu'est-ce que la fragmentation de la mémoire?
  • Comment savoir si la fragmentation de la mémoire est un problème pour mon application? Quel type de programme est le plus susceptible de souffrir?
  • Quels sont les bons moyens courants de gérer la fragmentation de la mémoire?

Aussi:

  • J'ai entendu dire que l'utilisation d'allocations dynamiques pouvait augmenter considérablement la fragmentation de la mémoire. Est-ce vrai? Dans le contexte de C ++, je comprends que tous les conteneurs standard (std :: string, std :: vector, etc.) utilisent l'allocation dynamique de mémoire. Si ceux-ci sont utilisés dans un programme (en particulier std :: string), la fragmentation de la mémoire est-elle plus susceptible d'être un problème?
  • Comment gérer la fragmentation de la mémoire dans une application lourde en STL?
AshleysBrain
la source
1
Beaucoup de bonnes réponses, merci à tous!
AshleysBrain
4
Il y a déjà beaucoup de bonnes réponses, mais voici quelques photos d'une application réelle (Firefox) où la fragmentation de la mémoire était un gros problème: blog.pavlov.net/2007/11/10/memory-fragmentation
Marius Gedminas
2
@MariusGedminas le lien ne fonctionne plus c'est pourquoi il est important de fournir un bref résumé avec le lien ou de répondre à la question avec un résumé avec le lien
katta
Bien sûr, mais cela fait plus d'une demi-décennie
rsethc
3
Vous trouverez ci-dessous un emplacement mis à jour pour les liens publiés par Marius: pavlovdotnet.wordpress.com/2007/11/10/memory-fragmentation
TheGameiswar

Réponses:

313

Imaginez que vous ayez une "grande" étendue (32 octets) de mémoire libre:

----------------------------------
|                                |
----------------------------------

Maintenant, allouez-en une partie (5 allocations):

----------------------------------
|aaaabbccccccddeeee              |
----------------------------------

Maintenant, libérez les quatre premières allocations mais pas la cinquième:

----------------------------------
|              eeee              |
----------------------------------

Maintenant, essayez d'allouer 16 octets. Oups, je ne peux pas, même s'il y en a presque le double gratuitement.

Sur les systèmes dotés de mémoire virtuelle, la fragmentation pose moins de problèmes que vous ne le pensez, car les grandes allocations doivent uniquement être contiguës dans l' espace d'adressage virtuel , pas dans l' espace d'adressage physique . Donc, dans mon exemple, si j'avais de la mémoire virtuelle avec une taille de page de 2 octets, je pourrais faire mon allocation de 16 octets sans problème. La mémoire physique ressemblerait à ceci:

----------------------------------
|ffffffffffffffeeeeff            |
----------------------------------

tandis que la mémoire virtuelle (étant beaucoup plus grande) pourrait ressembler à ceci:

------------------------------------------------------...
|              eeeeffffffffffffffff                   
------------------------------------------------------...

Le symptôme classique de la fragmentation de la mémoire est que vous essayez d'allouer un gros bloc et vous ne pouvez pas, même si vous semblez avoir suffisamment de mémoire disponible. Une autre conséquence possible est l'incapacité du processus à libérer de la mémoire sur le système d'exploitation (car il y a encore un objet en cours d'utilisation dans tous les blocs qu'il a alloués à partir du système d'exploitation, même si ces blocs sont maintenant pour la plupart inutilisés).

Tactiques pour empêcher la fragmentation de la mémoire dans le travail C ++ en allouant des objets de différentes zones en fonction de leur taille et / ou de leur durée de vie attendue. Donc, si vous allez créer beaucoup d'objets et les détruire tous ensemble plus tard, allouez-les à partir d'un pool de mémoire. Toutes les autres allocations que vous effectuez entre elles ne proviendront pas du pool, donc ne seront pas situées entre elles en mémoire, de sorte que la mémoire ne sera pas fragmentée en conséquence.

En règle générale, vous n'avez pas à vous en préoccuper, à moins que votre programme ne dure longtemps et fasse beaucoup d'allocation et de libération. C'est lorsque vous avez des mélanges d'objets de courte durée et de longue durée de vie que vous êtes le plus à risque, mais même alors, mallocil fera de son mieux pour vous aider. Fondamentalement, ignorez-le jusqu'à ce que votre programme ait des échecs d'allocation ou que le système manque de mémoire de manière inattendue (remarquez cela dans les tests, de préférence!).

Les bibliothèques standard ne sont pas pires qu'autre chose qui alloue de la mémoire, et les conteneurs standard ont tous un Allocparamètre de modèle que vous pouvez utiliser pour affiner leur stratégie d'allocation si cela est absolument nécessaire.

Steve Jessop
la source
1
Donc, chaque caractère est un octet? Ce qui rendrait votre "grande étendue" == 32 octets (je suppose - n'a pas compté) :) Bel exemple, mais mentionner les unités avant la dernière ligne serait utile. :)
jalf
1
@jalf: Oui. Je n'allais pas du tout mentionner les unités, puis j'ai réalisé à la fin que je devais le faire. J'y travaillais pendant que vous commentiez.
Steve Jessop
Il était assez difficile de choisir une «réponse» - beaucoup de bonnes réponses ici et j'encourage toute personne intéressée à les lire toutes. Pourtant, je pense que vous avez couvert tous les points importants ici.
AshleysBrain
1
"Les bibliothèques standard ne sont pas pires qu'autre chose qui alloue de la mémoire". Ce serait bien si cela était vrai, mais les implémentations de modèles C ++ standard tels que string & vector peuvent avoir des comportements très indésirables lors du redimensionnement. Par exemple, dans les anciennes versions de Visual Studio, la chaîne std :: se redimensionne essentiellement par realloc 1.5 * current_size (aux 8 octets les plus proches). Donc, si vous continuez à ajouter une chaîne, vous pouvez anhiler le tas très facilement, en particulier sur les systèmes embarqués. La meilleure défense consiste à réserver la quantité d'espace que vous prévoyez d'utiliser pour éviter les reallocs cachés.
locka
1
@ du369: La mémoire virtuelle n'est pas aussi fragmentée que le physique. ffffffffffffffffest une allocation contiguë dans la mémoire virtuelle, mais aucune telle allocation contiguë ne peut exister dans la mémoire physique. Si vous préférez le voir comme ils sont également fragmentés, mais l'espace virtuel est beaucoup plus grand, alors n'hésitez pas à le regarder de cette façon. Le point pratique important est que l'utilisation de vastes espaces d'adressage virtuels est souvent suffisante pour pouvoir ignorer la fragmentation, donc cela m'aide chaque fois qu'il me permet de faire mon allocation de 16 octets.
Steve Jessop
74

Qu'est-ce que la fragmentation de la mémoire?

La fragmentation de la mémoire se produit lorsque la majeure partie de votre mémoire est allouée dans un grand nombre de blocs non contigus, ou morceaux - laissant un bon pourcentage de votre mémoire totale non allouée, mais inutilisable pour la plupart des scénarios typiques. Cela entraîne des exceptions de mémoire insuffisante ou des erreurs d'allocation (c.-à-d. Malloc renvoie null).

La façon la plus simple d'y penser est d'imaginer que vous avez un grand mur vide sur lequel vous devez mettre des images de tailles différentes . Chaque image prend une certaine taille et vous ne pouvez évidemment pas la diviser en petits morceaux pour l'adapter. Vous avez besoin d'un endroit vide sur le mur, de la taille de l'image, sinon vous ne pouvez pas le mettre en place. Maintenant, si vous commencez à accrocher des images sur le mur et que vous ne faites pas attention à la façon dont vous les organisez, vous vous retrouverez bientôt avec un mur partiellement recouvert d'images et même si vous avez des espaces vides, la plupart des nouvelles images ne s'adapteront pas car ils sont plus grands que les spots disponibles. Vous pouvez toujours accrocher de très petites images, mais la plupart ne conviennent pas. Vous devrez donc réorganiser (compacter) ceux déjà sur le mur pour faire de la place pour plus ..

Maintenant, imaginez que le mur est votre (tas) mémoire et les images sont des objets .. C'est la fragmentation de la mémoire ..

Comment savoir si la fragmentation de la mémoire est un problème pour mon application? Quel type de programme est le plus susceptible de souffrir?

Un signe révélateur que vous pouvez être confronté à une fragmentation de la mémoire est que si vous obtenez de nombreuses erreurs d'allocation, en particulier lorsque le pourcentage de mémoire utilisée est élevé - mais que vous n'avez pas encore utilisé toute la mémoire -, techniquement, vous devriez avoir beaucoup de place pour les objets que vous essayez d'allouer.

Lorsque la mémoire est fortement fragmentée, les allocations de mémoire prendront probablement plus de temps car l'allocateur de mémoire doit faire plus de travail pour trouver un espace approprié pour le nouvel objet. Si, à son tour, vous disposez de nombreuses allocations de mémoire (ce que vous faites probablement depuis que vous vous êtes retrouvé avec une fragmentation de la mémoire), le temps d'allocation peut même provoquer des retards notables.

Quels sont les bons moyens courants de gérer la fragmentation de la mémoire?

Utilisez un bon algorithme pour allouer de la mémoire. Au lieu d'allouer de la mémoire à un grand nombre de petits objets, pré-allouez de la mémoire à un tableau contigu de ces petits objets. Parfois, un peu de gaspillage lors de l'allocation de mémoire peut favoriser les performances et vous éviter d'avoir à gérer la fragmentation de la mémoire.

Mike Dinescu
la source
10
+1. Je viens de supprimer ma réponse proposée parce que votre métaphore "photos sur le mur" est vraiment, vraiment bonne, claire.
ctacke
J'aimerais plus si vous insistez sur le fait que les images doivent avoir des tailles différentes. Sinon, aucune fragmentation ne se produira.
Björn Pollex,
1
Fait intéressant, les bases de données de mémoire principales deviennent quelque peu pratiques ces jours-ci (avec beaucoup de mémoire disponible). Dans ce contexte, il convient de noter que, comme pour les disques durs, la lecture de lignes continues à partir de la RAM est beaucoup plus rapide que si les données sont fragmentées.
Björn Pollex,
1
Belle analogie visuelle avec les images sur les murs, mais la mémoire principale n'est pas en deux dimensions! Pourtant, belle réponse cependant, merci.
AshleysBrain
24

La fragmentation de la mémoire est le même concept que la fragmentation du disque: elle se réfère au gaspillage d'espace car les zones utilisées ne sont pas suffisamment rapprochées.

Supposons pour un exemple de jouet simple que vous disposez de dix octets de mémoire:

 |   |   |   |   |   |   |   |   |   |   |
   0   1   2   3   4   5   6   7   8   9

Allouons maintenant trois blocs de trois octets, nommés A, B et C:

 | A | A | A | B | B | B | C | C | C |   |
   0   1   2   3   4   5   6   7   8   9

Désallouez maintenant le bloc B:

 | A | A | A |   |   |   | C | C | C |   |
   0   1   2   3   4   5   6   7   8   9

Maintenant, que se passe-t-il si nous essayons d'allouer un bloc de quatre octets D? Eh bien, nous avons quatre octets de mémoire libres, mais nous n'avons pas quatre octets contigus de mémoire libres, donc nous ne pouvons pas allouer D! Il s'agit d'une utilisation inefficace de la mémoire, car nous aurions dû pouvoir stocker D, mais nous n'avons pas pu. Et nous ne pouvons pas déplacer C pour faire de la place, car très probablement certaines variables de notre programme pointent vers C, et nous ne pouvons pas trouver et modifier automatiquement toutes ces valeurs.

Comment savez-vous que c'est un problème? Eh bien, le plus grand signe est que la taille de la mémoire virtuelle de votre programme est considérablement plus grande que la quantité de mémoire que vous utilisez réellement. Dans un exemple concret, vous auriez beaucoup plus de dix octets de mémoire, donc D serait simplement alloué à partir d'un octet 9, et les octets 3-5 resteraient inutilisés sauf si vous allouiez plus tard quelque chose de trois octets de long ou plus petit.

Dans cet exemple, 3 octets ne sont pas beaucoup à perdre, mais considérons un cas plus pathologique où deux allocations d'un couple d'octets sont, par exemple, dix mégaoctets en mémoire, et vous devez allouer un bloc de taille 10 mégaoctets + 1 octet. Vous devez aller demander au système d'exploitation plus de dix mégaoctets de mémoire virtuelle pour le faire, même si vous n'êtes qu'un octet de moins d'avoir déjà suffisamment d'espace.

Comment l'empêchez-vous? Les pires cas ont tendance à se produire lorsque vous créez et détruisez fréquemment de petits objets, car cela a tendance à produire un effet de "fromage suisse" avec de nombreux petits objets séparés par de nombreux petits trous, ce qui rend impossible d'allouer des objets plus gros dans ces trous. Lorsque vous savez que vous allez le faire, une stratégie efficace consiste à pré-allouer un grand bloc de mémoire en tant que pool pour vos petits objets, puis à gérer manuellement la création des petits objets dans ce bloc, plutôt que de laisser l'allocateur par défaut le gère.

En général, moins vous faites d'allocations, moins la mémoire est susceptible d'être fragmentée. Cependant, STL gère cela de manière assez efficace. Si vous avez une chaîne qui utilise l'intégralité de son allocation actuelle et que vous y ajoutez un caractère, elle ne se réalloue pas simplement à sa longueur actuelle plus un, elle double sa longueur. Il s'agit d'une variante de la stratégie du «pool pour les petites allocations fréquentes». La chaîne saisit une grande partie de la mémoire afin de pouvoir gérer efficacement les petites augmentations de taille répétées sans effectuer de petites réallocations répétées. En fait, tous les conteneurs STL font ce genre de chose, donc généralement vous n'aurez pas à vous soucier trop de la fragmentation causée par la réallocation automatique des conteneurs STL.

Bien que, bien sûr, les conteneurs STL ne regroupent pas la mémoire entre eux, donc si vous allez créer de nombreux petits conteneurs (plutôt que quelques conteneurs qui sont redimensionnés fréquemment), vous devrez peut-être vous préoccuper de prévenir la fragmentation de la même manière que vous serait pour tous les petits objets fréquemment créés, STL ou non.

Tyler McHenry
la source
14
  • Qu'est-ce que la fragmentation de la mémoire?

La fragmentation de la mémoire est le problème de la mémoire devenant inutilisable même si elle est théoriquement disponible. Il existe deux types de fragmentation: la fragmentation interne est une mémoire qui est allouée mais ne peut pas être utilisée (par exemple, lorsque la mémoire est allouée en blocs de 8 octets mais que le programme fait à plusieurs reprises des allocations uniques alors qu'il n'a besoin que de 4 octets). la fragmentation externe est le problème de la mémoire libre se divisant en plusieurs petits morceaux de sorte que les demandes d'allocation importantes ne peuvent pas être satisfaites bien qu'il y ait suffisamment de mémoire libre globale.

  • Comment savoir si la fragmentation de la mémoire est un problème pour mon application? Quel type de programme est le plus susceptible de souffrir?

la fragmentation de la mémoire est un problème si votre programme utilise beaucoup plus de mémoire système que ne l'exigent ses données de paylod réelles (et vous avez exclu les fuites de mémoire).

  • Quels sont les bons moyens courants de gérer la fragmentation de la mémoire?

Utilisez un bon allocateur de mémoire. IIRC, ceux qui utilisent une stratégie de «meilleur ajustement» sont généralement beaucoup plus efficaces pour éviter la fragmentation, si un peu plus lentement. Cependant, il a également été démontré que pour toute stratégie d'allocation, il existe des pires cas pathologiques. Heureusement, les modèles d'allocation typiques de la plupart des applications sont en fait relativement bénins à gérer pour les allocateurs. Il y a un tas d'articles si vous êtes intéressé par les détails:

  • Paul R. Wilson, Mark S. Johnstone, Michael Neely et David Boles. Allocation dynamique de stockage: une enquête et un examen critique. Dans les actes de l'atelier international de 1995 sur la gestion de la mémoire, Springer Verlag LNCS, 1995
  • Mark S.Johnstone, Paul R. Wilson. Le problème de fragmentation de la mémoire: résolu? Dans ACM SIG-PLAN Notices, volume 34 No. 3, pages 26-36, 1999
  • MR Garey, RL Graham et JD Ullman. Analyse du pire des cas des algorithmes d'allocation de mémoire. Dans le quatrième symposium annuel de l'ACM sur la théorie de l'informatique, 1972
Michael Borgwardt
la source
9

Mise à jour:
Google TCMalloc: Discussion-caching Malloc
Il a été constaté que il est assez bon à la manipulation fragmentation dans un processus en cours d' exécution longue.


J'ai développé une application serveur qui a rencontré des problèmes de fragmentation de la mémoire sur HP-UX 11.23 / 11.31 ia64.

Cela ressemblait à ça. Il y avait un processus qui a fait des allocations de mémoire et des désallocations et a fonctionné pendant des jours. Et même s'il n'y a eu aucune fuite de mémoire, la consommation de mémoire du processus a continué d'augmenter.

À propos de mon expérience. Sur HP-UX, il est très facile de trouver la fragmentation de la mémoire à l'aide de HP-UX gdb. Vous définissez un point d'arrêt et lorsque vous l'atteignez, vous exécutez cette commande: info heapet voyez toutes les allocations de mémoire pour le processus et la taille totale du tas. Ensuite, vous continuez votre programme et puis quelque temps plus tard, vous atteignez à nouveau le point d'arrêt. Vous recommencez info heap. Si la taille totale du tas est plus grande mais que le nombre et la taille des allocations distinctes sont les mêmes, il est probable que vous ayez des problèmes d'allocation de mémoire. Si nécessaire, effectuez cette vérification quelques fois à l'avance.

Ma façon d'améliorer la situation était la suivante. Après avoir effectué une analyse avec HP-UX gdb, j'ai vu que les problèmes de mémoire étaient dus au fait que j'utilisais std::vectorpour stocker certains types d'informations à partir d'une base de données. std::vectorexige que ses données soient conservées dans un seul bloc. J'avais quelques conteneurs basés sur std::vector. Ces conteneurs ont été recréés régulièrement. Il y avait souvent des situations où de nouveaux enregistrements étaient ajoutés à la base de données et ensuite les conteneurs étaient recréés. Et comme les conteneurs recréés étaient plus gros, ils ne correspondaient pas aux blocs de mémoire libre disponibles et le moteur d'exécution a demandé un nouveau bloc plus gros à partir du système d'exploitation. Par conséquent, même s'il n'y a eu aucune fuite de mémoire, la consommation de mémoire du processus a augmenté. J'ai amélioré la situation lorsque j'ai changé les conteneurs. Au lieu de std::vectorj'ai commencé à utiliserstd::deque qui a une façon différente d'allouer de la mémoire pour les données.

Je sais que l'un des moyens d'éviter la fragmentation de la mémoire sur HP-UX consiste à utiliser Small Block Allocator ou MallocNextGen. Sur RedHat Linux, l'allocateur par défaut semble gérer assez bien l'allocation d'un grand nombre de petits blocs. Sous Windows, il existe Low-fragmentation Heapet il résout le problème du grand nombre de petites allocations.

Je crois comprendre que dans une application lourde en STL, vous devez d'abord identifier les problèmes. Les allocateurs de mémoire (comme dans libc) gèrent en fait le problème de nombreuses petites allocations, ce qui est typique std::string(par exemple, dans mon application serveur, il y a beaucoup de chaînes STL mais, comme je le constate en exécutant, info heapelles ne posent aucun problème). Mon impression est que vous devez éviter de fréquentes allocations importantes. Malheureusement, il y a des situations où vous ne pouvez pas les éviter et devez changer votre code. Comme je l'ai dit dans mon cas, j'ai amélioré la situation lorsque je suis passé à std::deque. Si vous identifiez votre fragment de mémoire, il pourrait être possible d'en parler plus précisément.


la source
6

La fragmentation de la mémoire est plus susceptible de se produire lorsque vous allouez et désallouez de nombreux objets de tailles différentes. Supposons que vous ayez la disposition suivante en mémoire:

obj1 (10kb) | obj2(20kb) | obj3(5kb) | unused space (100kb)

Désormais, une fois obj2libéré, vous disposez de 120 Ko de mémoire inutilisée, mais vous ne pouvez pas allouer un bloc complet de 120 Ko, car la mémoire est fragmentée.

Les techniques courantes pour éviter cet effet incluent les tampons en anneau et les pools d'objets . Dans le contexte de la STL, des méthodes comme std::vector::reserve()peuvent aider.

Björn Pollex
la source
3

Qu'est-ce que la fragmentation de la mémoire?

Lorsque votre application utilise la mémoire dynamique, elle alloue et libère des morceaux de mémoire. Au début, tout l'espace mémoire de votre application est un bloc contigu de mémoire libre. Cependant, lorsque vous allouez et libérez des blocs de taille différente, la mémoire commence à être fragmentée , c'est-à-dire qu'au lieu d'un grand bloc libre contigu et d'un certain nombre de blocs alloués contigus, il y aura un mélange de blocs alloués et libres. Comme les blocs libres ont une taille limitée, il est difficile de les réutiliser. Par exemple, vous pouvez avoir 1000 octets de mémoire libre, mais vous ne pouvez pas allouer de mémoire pour un bloc de 100 octets, car tous les blocs libres ont une longueur maximale de 50 octets.

Une autre source de fragmentation inévitable, mais moins problématique, est que dans la plupart des architectures, les adresses mémoire doivent être alignées sur des limites d'octets de 2, 4, 8 etc. (c'est-à-dire que les adresses doivent être des multiples de 2, 4, 8, etc.). Cela signifie que même si vous avez par exemple une structure contenant 3 charchamps, votre structure peut avoir une taille de 12 au lieu de 3, car chaque champ est aligné sur une limite de 4 octets.

Comment savoir si la fragmentation de la mémoire est un problème pour mon application? Quel type de programme est le plus susceptible de souffrir?

La réponse évidente est que vous obtenez une exception de mémoire insuffisante.

Apparemment, il n'existe aucun bon moyen portable de détecter la fragmentation de la mémoire dans les applications C ++. Voir cette réponse pour plus de détails.

Quels sont les bons moyens courants de gérer la fragmentation de la mémoire?

C'est difficile en C ++, car vous utilisez des adresses mémoire directes dans les pointeurs et vous n'avez aucun contrôle sur qui référence une adresse mémoire spécifique. Réorganiser les blocs de mémoire alloués (comme le fait le ramasse-miettes Java) n'est pas une option.

Un allocateur personnalisé peut aider en gérant l'allocation de petits objets dans un plus gros morceau de mémoire et en réutilisant les emplacements libres dans ce morceau.

Péter Török
la source
3

Il s'agit d'une version super simplifiée pour les nuls.

À mesure que les objets sont créés en mémoire, ils sont ajoutés à la fin de la partie utilisée en mémoire.

Si un objet qui n'est pas à la fin de la partie de mémoire utilisée est supprimé, ce qui signifie que cet objet se trouvait entre 2 autres objets, cela créera un "trou".

C'est ce qu'on appelle la fragmentation.

user455288
la source
2

Lorsque vous souhaitez ajouter un élément sur le tas, ce qui se passe, c'est que l'ordinateur doit effectuer une recherche d'espace pour s'adapter à cet élément. C'est pourquoi les allocations dynamiques lorsqu'elles ne sont pas effectuées sur un pool de mémoire ou avec un allocateur poolé peuvent "ralentir" les choses. Pour une application STL lourde si vous faites du multi-thread, il y a l' allocateur Hoard ou la version TBB Intel .

Maintenant, lorsque la mémoire est fragmentée, deux choses peuvent se produire:

  1. Il faudra plus de recherches pour trouver un bon espace pour coller des "gros" objets. Autrement dit, avec de nombreux petits objets éparpillés pour trouver un joli morceau de mémoire contigus, cela pourrait être difficile dans certaines conditions (ils sont extrêmes).
  2. La mémoire n'est pas une entité facile à lire. Les processeurs sont limités à combien ils peuvent contenir et où. Ils le font en échangeant des pages si un élément dont ils ont besoin est un endroit mais les adresses actuelles en sont un autre. Si vous devez constamment échanger des pages, le traitement peut ralentir (encore une fois, des scénarios extrêmes où cela affecte les performances.) Voir cette publication sur la mémoire virtuelle .
blés
la source
1

La fragmentation de la mémoire se produit car des blocs de mémoire de tailles différentes sont demandés. Prenons un tampon de 100 octets. Vous demandez deux caractères, puis un entier. Vous libérez maintenant les deux caractères, puis vous demandez un nouvel entier, mais cet entier ne peut pas tenir dans l'espace des deux caractères. Cette mémoire ne peut pas être réutilisée car elle n'est pas dans un bloc contigu suffisamment grand pour être réalloué. En plus de cela, vous avez invoqué de nombreux frais généraux d'allocateur pour vos caractères.

Essentiellement, la mémoire ne vient que par blocs d'une certaine taille sur la plupart des systèmes. Une fois que vous avez divisé ces blocs, ils ne peuvent pas être rejoints jusqu'à ce que le bloc entier soit libéré. Cela peut conduire à l'utilisation de blocs entiers alors qu'en réalité seule une petite partie du bloc est utilisée.

Le principal moyen de réduire la fragmentation du segment de mémoire consiste à effectuer des allocations plus importantes et moins fréquentes. À l'extrême, vous pouvez utiliser un tas géré qui est capable de déplacer des objets, au moins, dans votre propre code. Cela élimine complètement le problème - du point de vue de la mémoire, de toute façon. Évidemment, les objets en mouvement et autres ont un coût. En réalité, vous n'avez vraiment de problème que si vous allouez souvent de très petites quantités du tas. L'utilisation de conteneurs contigus (vecteur, chaîne, etc.) et l'allocation sur la pile autant que possible humainement (toujours une bonne idée de performances) est la meilleure façon de la réduire. Cela augmente également la cohérence du cache, ce qui accélère l'exécution de votre application.

Ce que vous devez retenir, c'est que sur un système de bureau 32 bits x86, vous disposez de 2 Go de mémoire, qui sont divisés en "pages" de 4 Ko (assez sûr que la taille de la page est la même sur tous les systèmes x86). Vous devrez invoquer une certaine fragmentation omgwtfbbq pour avoir un problème. La fragmentation est vraiment un problème du passé, car les tas modernes sont excessivement grands pour la grande majorité des applications, et il existe une prévalence de systèmes capables de le supporter, tels que les tas gérés.

Chiot
la source
0

Quel type de programme est le plus susceptible de souffrir?

Un bon (= horrible) exemple pour les problèmes associés à la fragmentation de la mémoire a été le développement et la sortie de "Elemental: War of Magic" , un jeu informatique de Stardock .

Le jeu a été conçu pour une mémoire de 32 bits / 2 Go et a dû faire beaucoup d'optimisation dans la gestion de la mémoire pour que le jeu fonctionne avec ces 2 Go de mémoire. L'optimisation entraînant une allocation et une désallocation constantes, une fragmentation de la mémoire au fil du temps s'est produite et a entraîné le crash du jeu à chaque fois .

Il y a une interview sur "l'histoire de la guerre" sur YouTube.

Thomas
la source