Les problèmes de l'utilisation et la réutilisation de nombreux objets de taille variable dans une application peut provoquer la fragmentation du tas qui peuvent ralentir la vitesse de traitement. Cet article utilise un objet fabrique, de faire et de recycler les objets avec un minimum d'effets de fragmentation.
L'un des modèles classiques dans le livre "Design Patterns, Elements of Reusable Object-Oriented Software" est la méthode, où les objets sont créés et j'ai récemment eu à l'utiliser. La raison? Un problème de fragmentation de la mémoire dans un système de simulation en utilisant un grand nombre d'objets de tenir des tableaux numériques. Il existe plusieurs types de tableau numérique, dans des formats fixes variant de 1500 x 1 à 5000 x 10, soit des entiers ou des numéros doubles. Celles-ci ont été utilisées dans une simulation d'un calcul financier sur une période de deux ans, la création de données sur chacun des 472 jours de simulation des données historiques. À chaque jour, des données ont été lus à partir d'une base de données, traitées, puis sauvé par.Malgré de nombreuses fuites de vérification, le logiciel ne fonctionnera que pour un certain nombre de jours avant de manger simulé le fichier d'échange Windows. Des fuites de mémoire ont été soigneusement recherchés et éliminés, mais mes cheveux étaient encore en danger. Le soft ne fonctionne pas pendant plus de 30 jours avant la simulation, il a commis plus de la moitié de la NT, ce fichier de pagination sur un système de 512 Mo de ram! Fermer enquête a montré que chaque jour il a couru, la mémoire allouée est passée de 5 millions à environ 80Mo ensuite reculer à environ 5 millions de nouveau. Sur le visage de celui-ci, pas un problème avec 512 Mo de jouer avec, mais regardez le Gestionnaire des tâches de Windows NT ont montré une augmentation de la quantité de mémoire commis.
FRAGMENTATION
Le problème est simple, il a été la fragmentation de segment à blâmer. Cela se produit lorsque de nombreux objets sont créés, puis détruits de façon répétée. Comme chaque objet est créé, il consomme de la mémoire heap. Si les objets ont été créés, puis détruits dans l'ordre inverse, il n'aurait probablement pas eu lieu comme tous les libérés de mémoire pourraient être fusionnés en un seul gros bloc. Mais dans un système avec un grand nombre d'objets de l'ordre de la création et la destruction ne sera jamais mon app-symétrique peut facilement avoir jusqu'à 50.000 objets en mémoire en même temps. Alors, quand un objet est détruit, un pointeur vers le bloc de mémoire libéré est ajouté à une zone de libre-bloc de la liste.Lorsque d'autres demandes sont faites pour la mémoire, Windows tente de renvoyer ces demandes de libération de la première liste. La fragmentation se produit lorsque un gros bloc comme 1Mb a été demandé et ensuite libérées, suivie d'une demande pour un petit bloc. Il s'agit du premier bloc sur la liste de libre-mai-être la 1Mb, qui ne laisse que 900Kb libre. Ensuite, une autre demande pour une grande 1Mb bloc se présente, il ne peut pas être satisfait de la liste libre, et il est tiré du tas. Si la création / destruction du cycle se produit suffisamment de fois les grands blocs sur le tas sont coupés en petits morceaux, la mémoire physique est épuisé et remplacé par virtuelle ram. Windows tas de gestion de la mémoire (et Delphi) est assez intelligent, il faut beaucoup pour qu'il fragment. Mais sous la pression incessante d'un grand nombre d'objets créés et détruits, et le gestionnaire de mémoire sera progressivement grotte po
Lorsque Windows est à cours de RAM, il commence par l'échange de pages de mémoire vive sur le disque et la performance a un nez de plongée. Votre application peut galoper joyeusement le long de CPU à 100% jusqu'à ce que l'échange commence. Il devient alors un enterrement mars peut-être à l'exploration le long de 7-10% de la CPU, en tenant toujours à courir. Désastre!
Microsoft a mis beaucoup d'effort de rendre le gestionnaire de mémoire aussi efficace que possible. Par exemple, sous NT, il existe un processus en deux étapes et l'engagement de réserver la mémoire. Si votre application exige 100Mb, il est réservé lorsque l'application est chargée. Mais ce n'est que lorsque la mémoire est accessible de la réserve sont engagés pages. Si vous voulez en savoir plus que vous aurez éventuellement besoin de savoir à ce sujet et d'autres sujets, je vous recommande le livre Inside NT, publié par Microsoft Press, mais obtenir la version de David Solomon, qui est la dernière édition, pas la première édition Helen Custer.
PATTERN FACTORY
J'avais donc besoin de fragmenter un moyen de créer de nombreux objets, de les utiliser, de les jeter, puis faire de nouveau sans fenêtres à court de mémoire virtuelle. Ayant lu récemment le livre Pattern je pensais pourquoi ne pas utiliser une usine, c'est-à-dire un objet que l'usine crée des objets d'une catégorie. J'ai ensuite un mieux et il est respectueux de l'environnement, de sorte qu'il est de recycler l'ensemble de ses objets manufacturés au lieu de les détruire et sans les problèmes de fragmentation. La cerise sur le gâteau, est de faire de l'usine en mesure d'étendre sa capacité, sans perte de vitesse d'accès.
Plutôt que d'avoir une usine de classe pour tous les types de classe, j'ai pris le plus simple de passer de la classe d'objets dans l'usine comme une usine de création de paramètre. Lorsque l'usine est créée, vous indiquer la classe d'objets qu'il peut apporter et la première capacité de stockage de l'usine. Cette taille peut être changée en appelant la hausse GrowFactory méthode. Je vous suggère d'appeler ce seulement à titre exceptionnel (!) Les circonstances.
Le lien de retour à l'usine de chaque objet est nécessaire, afin que tous "les objets d'usine" (FMOs) doit descendre d'une classe à la place de TFactoryObject TObject. Cela ajoute une usine de référence, qui est "marqué" sur tous les FMOs de sorte que l'objet qui sait utiliser l'usine de recyclage lui-même.
Au lieu de créer un objet de votre demande environ une heure appropriée de l'usine en appelant sa RequestObj méthode qui retourne un objet tclass retour et vous convertir à la bonne classe en utilisant «comme». Enfin, lorsque vous avez fini d'utiliser l'objet que vous suffit d'appeler sa méthode RecycleSelf. Non, sauf la création ou la destruction des usines elles-mêmes.
COMMENT ÇA MARCHE?
Lorsque l'usine est créé, tous les objets physiques sont créés dans un bloc continu de ram. A tlist (fblocklist) objet contient l'adresse de chacun de ces blocs. Chaque fois que vous cultivez l'usine, un nouveau bloc est créé et ajouté à cette liste. La méthode AddObjects crée le nombre d'objets à l'aide du bloc de la ram. Si vous écrivez du code, comme être conscient de ce qui vient de faire un TObject (adresse) ne suffit pas à créer l'objet. Vous devez toujours faire appel ObjectClass.InitInstance (adresse) pour le transformer en un "bon" objet. InitInstance efface tout à zéro, zéro, etc, mais surtout il met en place le VMT.
L'usine contient également un autre tlist (ffreelist) qui contient l'adresse de tous les objets non utilisés.
Tous les travaux de l'âne de remplissage de l'usine se fait dans le privé, la méthode AddObjects. Pour chaque objet créé dans le bloc, ce que le pointeur ptr transforme en un objet, en utilisant FactoryObject de la classe de l'objet créé. Il doit toujours être un descendant de TfactoryObject. Obj détient l'objet de référence et des liens vers l'usine à l'objet fabriqué. ptr est incrémenté pour pointer sur l'objet suivant dans le bloc, en ajoutant fsize.
Pour obtenir un code objet que vous r appelle Request_Obj qui apparaît la référence à l'extérieur de l'ffreelist et le renvoie en tant que premier objet demandé. Le recyclage est l'inverse, elle pousse le recyclés objet de référence sur la fin de ffreelist. Une chose à garder à l'esprit. Lorsque l'usine a fait l'objet est en cours d'utilisation, l'usine n'a pas de référence à celle-ci, bien que la mémoire occupée par l'objet est contenue dans l'usine!
REMPLACEMENT créer et détruire des
Utilisation de l'usine nécessite l'utilisation d'objets manufacturés sont légèrement différentes de la normale. Il n'est plus libre de créer ou de façon explicite, au lieu que vous venez juste de demander aux usines de l'objet. Sauf si l'usine est toujours vide ce travail. Vous devez modifier le code d'initialisation dans le constructeur et de la fin du code dans le destructeur de routines. Il ya deux approches.
1) Si l'objet est de créer un constructeur sans paramètres, vous pouvez le renommer à la procédure d'initialisation; annuler et de supprimer tout hérité de créer des appels. L'usine a toujours appelle une méthode d'initialisation lorsque l'objet est demandé. Par défaut, ce n'est rien, mais vous pouvez remplacer ce que votre Init sera appelée automatiquement à chaque objet de la demande de l'usine.
2) Si votre original est de créer les paramètres, la renommer en quelque chose comme Initialise et de supprimer les appels hérité etc. Après l'objet est demandé appel initialiser la routine, par exemple MyRoutine.Initialise (...)
Si l'objet a destructor code, renommez-le à la procédure Fait; remplacer de sorte qu'il est appelé automatiquement lorsque l'objet est recyclé. Fait Init et sont similaires afin de créer ou de détruire, mais sans les bagages de la construction ou la destruction mécanisme.
Comme une légère digression, je comprends qu'il ya des arguments dans le monde en faveur de Delphi sur une partie ou de la création en deux parties Une partie est, où le constructeur a complètement des paramètres et crée l'objet. Dans l'approche en deux volets, le constructeur crée simplement un objet vide qui est alors initialisé par une méthode plus tard. L'usine, je pense que l'approche est résolument dans le camp en deux parties ..
Lorsque l'usine est détruite, tous les blocs de mémoire alloués sont libérés. Avant cela, l'usine vérifie que le nombre d'objets dans le freelist correspond à la capacité. Si vous avez oublié de recycler l'ensemble de vos autres objets, il va soulever une exception.
PROGRAMME DE COMPARAISON
Cela montre les avantages d'usines .. Au travail, mon application a de nombreuses tailles d'objets dans des quantités différentes, mais un programme en ligne de 15000 ne serait pas exactement être publiable. Après un peu d'essais et d'erreurs je suis venu avec un petit programme qui peut montrer la fragmentation. Toutefois, cela dépend de la taille des objets différents, combien il ya de RAM disponible et pour combien de temps il est géré. Aussi, la fragmentation semble se produire plus rapidement sur NT4.0 de 98 ce qui suggère que, peut-être 98 a un meilleur gestionnaire de mémoire. Lors de son exécution, elle crée et détruit un grand nombre d'objets à maintes reprises pour le nombre de jours spécifié. Il a également fait exactement la même chose en utilisant une usine. Chaque journée est chronométrée et les deux séries de fois tracé en utilisant Tchart.J'ai utilisé trois types d'objets, tous les descendants de tTestobj qui descend de Tfactoryobject. 2K sont de petits objets de taille moyenne sont 40K et 800K sont grandes, mais ces formats sont définis par des constantes et peut facilement changé. Le programme de démo alloue 100Mb pour le normal et l'autre des objets 100Mb de l'usine. Contiennent tous deux le même nombre d'objets répartis également selon la taille entre les trois types d'objets sont donc des objets de petite taille 17406, 873 moyennes et grandes 43. Dans la procédure de création de l'objet TimeOneDay sont créés de façon aléatoire et ajoutés à la liste. À la fin de la journée, ils sont libérés et le processus répété le lendemain.La même chose est fait en utilisant trois usines avec tous les objets manufacturés factorydata ajouté à une liste. Sur un P2 400 avec 256Mo de fonctionnement de 100 jours, il ya eu une modeste augmentation dans le temps à la fois pour l'usine et la création de l'objet de la normale. Commentant la création de la normale, a confirmé ces soupçons, l'augmentation a été beaucoup moins de plus de 1000 jours lancé en utilisant les usines contre 100 jours à la fois normal et les tests en usine. J'ai deviné que cette augmentation a été causée par la page d'échange en raison de la fragmentation qui affectent les deux processus. D'autres combinaisons de tailles et de nombre d'objets sous Windows 98 ne présentaient aucune fragmentationJ'ai mis en place cette aide d'une tlist initialement pour les deux testdata et factorydata et modifiée plus tard, lorsque je me suis rendu à maintes reprises que l'ajout de 18.000 et de libérer des pointeurs est également d'ajouter à la fragmentation du tas. Je ne plaide pas pour le creusement de fossés tstringlists complètement dans votre code, ils sont très utiles (comme le sont tlists), mais si vous avez à manipuler un grand nombre d'éléments, il pourrait être préférable d'utiliser votre propre liste de structures. Si vous voulez utiliser tlist ou Tstringlist à cette fin, probablement sa meilleure de remplir complètement la structure dans le néant des pointeurs, de sorte que count = capacité et de l'utilisation de tenir un nombre entier de l'indice de la dernière pointeur.
Cette application a également confirmé que le code d'usine est très rapide à l'attribution et de suppression, généralement de 30 ms au lieu de 18.000 objets de 870 ms ce normal de création / suppression a eu.
------------------------------------------------
TGIS article paru dans le magazine pour développeurs Delphi. Sources.zip

Delicious
Digg
Google
Yahoo