2009-05-14 9 views
1

J'ai ce que je suppose être un problème de fragmentation de la mémoire.Fragmentation de mémoire WPF

Nous avons récemment porté notre application WinForms dans une application WPF. Il y a un traitement d'image que cette application fait, et ce traitement a toujours fonctionné dans la version WinForms de l'application. Nous allons à WPF, et le traitement meurt. Le débogage dans la bibliothèque a la mort à des endroits aléatoires, mais toujours avec un tableau qui est annulé, c'est-à-dire que l'allocation a échoué.

Le traitement lui-même est effectué dans une bibliothèque C++ appelée par p/invoke et est assez intense en mémoire; Si l'image donnée est N x M pixels gros, alors l'image est N x M x 2 octets gros (chaque pixel est un court-métrage non signé, et c'est une image en niveaux de gris). Pendant le traitement, les pyramides d'image sont faites, qui sont dans l'espace flottant, de sorte que l'utilisation totale de la mémoire sera N x M x (2 + 2 + 4 + 4 + 4 + 4), où le premier 2 est l'entrée, le second 2 est la sortie, le premier 4 est l'entrée dans les flottants, le deuxième 4 est l'image de la différence de 0e niveau, et les deux derniers quatre sont le reste de la pyramide (car ils sont des pyramides et chaque niveau est la moitié de la taille dans chaque direction, ces 4 sont des bornes supérieures). Donc, pour une image 5000x6000, c'est 600 Mo, ce qui devrait rentrer dans la mémoire très bien.

(Il est possible que l'utilisation de marshaling augmente la mémoire requise par un autre N x M x 4, c'est-à-dire les images d'entrée et de sortie du côté C# puis les mêmes tables copiées au C++. est-ce que le besoin est plus grand?)

Est-ce que WPF est fragmenté par rapport à WinForms? Existe-t-il un moyen de consolider la mémoire avant d'exécuter ce traitement? Je soupçonne que la fragmentation est le problème en raison de la nature aléatoire des ruptures, quand elles se produisent, et que c'est toujours un problème d'allocation de mémoire.

Ou devrais-je éviter complètement ce problème en faisant fonctionner le traitement comme un processus séparé, avec le transfert de données via des sockets ou quelque chose de similaire?

Répondre

3

Si je lis correctement, l'échec d'allocation de mémoire se produit du côté non géré, pas du côté géré. Il semble étrange de blâmer WPF. Je reconnais que vous tirez votre conclusion basée sur le fait que "cela a fonctionné dans WinForms", mais il y a probablement plus de changements que cela. Vous pouvez utiliser un outil tel que .NET Memory Profiler pour voir les différences entre la façon dont l'application WPF et l'application WinForms traitent la mémoire. Vous pourriez constater que votre application fait quelque chose que vous n'attendez pas. :)

Par commentaire: Oui, je comprends. Si vous êtes sûr que vous avez exclu des choses comme les changements d'environnement, je pense que vous devez saisir une copie de BoundsChecker et Memory Profiler (ou DevPartner Studio) et de creuser et voir ce qui gâche votre allocation de mémoire.

+0

Je suis allé sur ce code de traitement plusieurs fois. S'il fuit ou se comporte mal, je ne le vois pas; mais les pauses ont vraiment commencé à se produire lorsque nous sommes allés à WPF à partir de winforms. Le code est par ailleurs identique. – mmr

1

Je suppose que le GC déplace votre mémoire. Essayez d'épingler la mémoire dans un terrain non géré tant que vous avez un pointeur brut vers le tableau, et décochez-le dès que possible. Il est possible que WPF fasse fonctionner le GC plus souvent, ce qui expliquerait pourquoi cela arrive plus souvent avec lui, et si c'est le GC, cela expliquerait pourquoi cela arrive à des endroits aléatoires dans votre code. Editer: Par curiosité, pourriez-vous également pré-allouer toute votre mémoire à l'avance (je ne vois pas le code, donc ne sais pas si c'est possible), et assurez-vous que tous vos pointeurs sont Non-null, donc vous pouvez vérifier que cela se passe réellement dans l'allocation de mémoire, plutôt que d'autres problèmes?

+0

épinglage? La DLL déclare toute sa mémoire de travail à l'intérieur de lui-même. Si le GC se promène un peu partout, alors cela voudrait vraiment dire qu'il faut aller dans un autre processus, juste pour éviter un tel comportement bancal. – mmr

+0

Eh bien, les pointeurs vers les objets * gcnew *, ou * nouveaux * objets? Je supposais que vous preniez un int * aux données de pixels brutes sur une classe Bitmap. Le GC pourrait ramasser le bitmap et le déplacer pendant que vous courez. – FryGuy

+0

ouais, je ne fais pas ça. J'utilise des images DICOM, qui ne sont pas des bitmaps. La visualisation est faite comme des textures openGL, et les images elles-mêmes ne sont que des tableaux - dans l'application C#, ils sont ushorts, et dans la DLL C++, ils deviennent des flottants, afin d'avoir des valeurs négatives et une dynamique suffisante. Donc, ils sont à de «nouveaux» objets. – mmr

0

Il semble que vous vouliez faire plus attention à votre gestion de la mémoire en général; c'est-à-dire soit exécuter le moteur de traitement dans un espace d'adresse séparé qui gère soigneusement la mémoire, soit pré-allouer un segment suffisamment grand avant que la mémoire ne soit trop fragmentée et gérer les images dans cette zone seulement. Si vous partagez un espace d'adressage avec le.NET Runtime dans un processus de longue durée, et vous avez besoin de grandes zones contiguës, il va toujours potentiellement échouer à un moment donné. Juste mon 2c.

+0

Donc, fondamentalement, juste aller à un deuxième processus est ce que j'entends. Pourquoi cela pourrait-il toujours échouer? Qu'est-ce que .NET fait ici qui le rend si instable? – mmr