Raisons possibles pour FileStream.Write () de générer une exception OutOfMemoryException?

J’ai 10 threads écrivant des milliers de petits tampons (16-30 octets chacun) dans un fichier énorme dans des positions aléatoires. Certains des threads lèvent OutOfMemoryException lors de la création de FileStream.Write ().

Quelle est la cause de l’exception OutOfMemoryException? Ce qu’il faut chercher?

J’utilise FileStream comme ceci (pour chaque élément écrit – ce code fonctionne à partir de 10 threads différents):

using (FileStream fs = new FileStream(path, FileMode.OpenOrCreate, FileAccess.Write, FileShare.ReadWrite, BigBufferSizeInBytes, FileOptions.SequentialScan)) { ... fs.Write(); } 

Je soupçonne que tous les tampons alloués dans FileStream ne sont pas libérés à temps par le GC. Ce que je ne comprends pas, c’est pourquoi le CLR, au lieu de lancer, n’exécute pas simplement un cycle GC et libère tous les tampons inutilisés?

Si dix threads ouvrent des fichiers comme l’indique votre code, vous disposez d’un maximum de dix objects FileStream non modifiés à la fois. Oui, FileStream a un tampon interne, dont vous indiquez la taille avec “BigBufferSizeInBytes” dans votre code. Pourriez-vous s’il vous plaît divulguer la valeur exacte? Si cela est suffisant (par exemple, environ 100 Mo), cela pourrait bien être la source du problème.

Par défaut (c’est-à-dire lorsque vous ne spécifiez pas de nombre lors de la construction), cette mémoire tampon est de 4 Ko et convient généralement à la plupart des applications. En général, si vous vous souciez vraiment des performances d’écriture sur disque, vous pouvez augmenter ce nombre à quelques 100 Ko, mais pas plus.

Toutefois, cela n’aurait aucun sens pour votre application spécifique, car ledit tampon ne contiendra jamais plus de 16 à 30 octets que vous y écrivez avant que vous ne disposiez de l’object FileStream avec Dispose ().

Pour répondre à votre question, une exception OutOfMemoryException est levée uniquement lorsque la mémoire demandée ne peut pas être allouée après l’ exécution d’un catalogue global. Encore une fois, si le tampon est vraiment volumineux, le système pourrait avoir beaucoup de mémoire, mais pas un bloc contigu . En effet, le tas d’objects volumineux n’est jamais compacté.

Je l’ai déjà rappelé à quelques resockets à des personnes, mais le tas d’objects volumineux peut émettre cette exception de manière assez subtile, alors que vous semblez avoir suffisamment de mémoire disponible ou que l’application fonctionne correctement.

Je me suis souvent heurté à ce problème en faisant presque exactement ce que vous décrivez ici.

Vous devez publier davantage de votre code pour répondre correctement à cette question. Cependant, je suppose que cela pourrait également être lié à un problème potentiel de Halloween (Spooky Dooky) .

Votre mémoire tampon à partir de laquelle vous lisez peut également être le problème (encore une fois lié au tas d’objects volumineux), vous avez également besoin de fournir plus de détails sur ce qui se passe là-bas dans la boucle. Je viens juste de clouer le dernier bogue que j’ai eu, qui est pratiquement identique (j’effectue de nombreuses mises à jour de hachage en parallèle qui nécessitent toutes un état indépendant pour être maintenues entre les lectures du fichier d’entrée) ….

OOP! vient de faire défiler et remarqué “BigBufferSizeInBytes”, je me penche à nouveau vers Large Object Heap …

Si j’étais vous (et cela est extrêmement difficile en raison du manque de contexte), je vous fournirais une petite dépêche “mbuf”, dans laquelle vous copiez des entrées et des sorties au lieu de permettre à tous vos threads dispersés d’être lus individuellement sur votre large support. tableau … (c’est-à-dire qu’il est difficile de ne pas provoquer d’allocations insadentielles avec une syntaxe de code très subtile).

Les tampons ne sont généralement pas alloués à l’intérieur du FileStream. Peut-être que le problème est la ligne “écrivant des milliers de petits tampons” – voulez-vous vraiment dire cela? Normalement, vous réutilisez un tampon plusieurs fois (c.-à-d. Lors d’appels différents en lecture / écriture).

Aussi – est-ce un fichier unique? Un seul FileStream n’est pas sûr d’être thread-safe … donc, si vous n’effectuez pas de synchronisation, attendez-vous au chaos.

Il est possible que ces limitations proviennent du système d’exploitation sous-jacent et que .NET Framework soit impuissant pour surmonter ce type de limitations.

Ce que je ne peux pas déduire de votre exemple de code, c’est de savoir si vous ouvrez beaucoup de ces objects FileStream en même temps, ou si vous les ouvrez très rapidement en séquence. Votre utilisation du mot-clé ‘using’ garantira que les fichiers sont fermés après l’appel de fs.Write (). Aucun cycle GC n’est requirejs pour fermer le fichier.

La classe FileStream est vraiment orientée vers un access séquentiel en lecture / écriture aux fichiers. Si vous avez besoin d’écrire rapidement à des emplacements aléatoires dans un fichier volumineux, vous pouvez envisager d’utiliser le mappage de fichier virtuel.

Mise à jour: Il semble que le mappage de fichier virtuel ne sera pas officiellement pris en charge dans .NET avant la version 4.0. Vous voudrez peut-être jeter un coup d’œil aux implémentations tierces pour cette fonctionnalité.

Dave

Je vis quelque chose de similaire et je me demandais si vous aviez déjà épinglé la racine de votre problème?

Mon code effectue beaucoup de copie entre fichiers, en passant quelques Mo entre différents fichiers d’octets. J’ai remarqué que, même si l’utilisation de la mémoire de processus rest dans une plage raisonnable, l’allocation de mémoire système augmente considérablement pendant la copie – bien plus que ce que mon processus utilise.

J’ai suivi le problème jusqu’à l’appel de FileStream.Write (). Lorsque cette ligne est supprimée, l’utilisation de la mémoire semble se dérouler comme prévu. Mon BigBufferSizeInBytes est la valeur par défaut (4k), et je ne vois nulle part où ils pourraient être collectés …

Tout ce que vous découvririez en regardant votre problème serait reçu avec gratitude!