C#: exception de mémoire

Aujourd'hui, ma demande a lancé un OutOfMemoryException . Pour moi, cela a toujours été presque impossible puisque j'ai 4 Go de RAM et beaucoup de mémoire virtuelle aussi. L'erreur qui s'est passé quand j'ai essayé d'ajouter une collection existante à une nouvelle liste.

List<Vehicle> vList = new List<Vehicle>(selectedVehicles);  

à ma connaissance il n'y a pas beaucoup de mémoire allouée ici puisque les véhicules que ma nouvelle liste devrait contenir existent déjà à l'intérieur de la mémoire. Je dois admettre que Vehicle est une classe très complexe et j'ai essayé d'ajouter 50.000 articles à la nouvelle liste à la fois. Mais puisque tous les Vehicle s dans l'application proviennent d'une base de données qui est seulement 200 Mo de taille: je n'ai aucune idée de ce qui peut causer un OutOfMemoryException à ce point.

54
demandé sur Wai Ha Lee 2011-12-19 19:59:38

10 réponses

deux points:

  1. si vous utilisez une fenêtre 32 bits, vous n'aurez pas tous les 4 Go accessibles, seulement 2 Go.
  2. N'oubliez pas que l'implémentation sous-jacente de List est un tableau. Si votre mémoire est fortement fragmentée, il peut ne pas y avoir assez d'espace contigu pour allouer votre List , même si au total vous avez beaucoup de mémoire libre.
63
répondu Tudor 2011-12-19 16:15:02

sujet vieux de 3 ans, mais j'ai trouvé une autre solution de travail. Si vous êtes sûr d'avoir assez de mémoire libre, d'exécuter un OS 64 bits et d'avoir toujours des exceptions, assurez-vous de mettre cette option dans les propriétés de votre projet. enter image description here

71
répondu throwExceptionDevelopment 2014-03-23 15:27:39

.Net4.5 n'a plus de limite de 2 Go pour les objets. Ajouter cette ligne à App.config

<runtime>
    <gcAllowVeryLargeObjects enabled="true" />    
</runtime>

et il sera possible de créer de très grands objets sans obtenir OutOfMemoryException

s'il vous Plaît noter qu'il ne fonctionne que sur le système d'exploitation x64!

63
répondu Lendmann 2014-01-03 21:19:23

données stockées dans la base de données par rapport à la mémoire dans votre application est très différent.

il n'y a aucun moyen d'obtenir la taille exacte de votre objet, mais vous pouvez le faire:

GC.GetTotalMemory() 

après qu'un certain nombre d'objets ont été chargés et voyez combien votre mémoire change pendant que vous chargez la liste.

si c'est la liste qui est à l'origine de l'utilisation excessive de la mémoire, alors nous pouvons chercher des moyens de la minimiser. Comme pourquoi voulez-vous que 50 000 objets soient chargés en mémoire d'un seul coup? Ne serait-il pas préférable d'appeler le DB comme vous le souhaitez?

si vous regardez ici: http://www.dotnetperls.com/array-memory vous verrez aussi que les objets en .NET sont plus grands que leurs données réelles. Une liste générique est encore plus d'un dévoreur de mémoire qu'un tableau. Si vous avez une liste générique à l'intérieur de votre objet alors il se développera encore plus rapidement.

11
répondu Adam Pedley 2011-12-19 16:11:48

OutOfMemoryException (sur les machines 32 bits) est tout aussi souvent à propos de la Fragmentation que les limites dures réelles sur la mémoire - vous trouverez beaucoup à ce sujet, mais voici mon premier google hit brièvement en discuter: http://blogs.msdn.com/b/joshwil/archive/2005/08/10/450202.aspx . (@Anthony Pegram fait référence au même problème dans son commentaire ci-dessus).

cela dit, il y a une autre possibilité qui vient à l'esprit pour votre code ci-dessus: le constructeur " IEnumerable "à la liste, vous peut ne donnant à l'objet aucun indice quant à la taille de la collection que vous passez au constructeur de liste. Si l'objet que vous passez n'est pas une collection (n'implémente pas l'interface ICollection ), alors dans les coulisses l'implémentation de liste va avoir besoin de se développer plusieurs fois (ou plusieurs), chaque fois laissant derrière un tableau trop petit qui doit être ramassé. Le ramasseur d'ordures probablement vous n'atteindrez pas ces tableaux assez vite, et vous obtiendrez votre erreur.

la solution la plus simple pour cela serait d'utiliser le constructeur List(int capacity) pour indiquer au framework quelle taille de tableau de soutien allouer (même si vous estimez et devinez juste" 50000 "par exemple), et puis utiliser la méthode AddRange(IEnumerable collection) pour peupler réellement votre liste.

donc, plus simple" Fix "si j'ai raison: remplacer

List<Vehicle> vList = new List<Vehicle>(selectedVehicles);

avec

List<Vehicle> vList = new List<Vehicle>(50000);  
vList.AddRange(selectedVehicles);

Tous les autres commentaires et réponses s'appliquent toujours en termes de conception globale décisions - mais cette pourrait être une solution rapide.

Note (comme @Alex a commenté ci-dessous), ce n'est qu'un problème si selectedVehicles n'est pas une ICollection.

7
répondu Tao 2011-12-19 16:45:36

mon équipe de développement a résolu cette situation:

nous avons ajouté le script de post-construction suivant dans le .exe project et compilé à nouveau, en fixant la cible à x86 et en augmentant de 1,5 Go et aussi la cible de la plate-forme x64 en augmentant la mémoire en utilisant 3,2 Go. Notre application est 32 bits.

URLs Connexes:

Script:

if exist "$(DevEnvDir)..\tools\vsvars32.bat" (
    call "$(DevEnvDir)..\tools\vsvars32.bat"
    editbin /largeaddressaware "$(TargetPath)"
)
4
répondu Mehmet Kurt 2017-05-23 12:10:34

vous ne devriez pas essayer d'apporter toute la liste à la fois, la taille des éléments dans la base de données n'est pas la même que celle qu'il prend en mémoire. Si vous voulez traiter les éléments, vous devez utiliser a pour chaque boucle et profiter de l'entity framework lazy loading de sorte que vous n'apportez pas tous les éléments dans la mémoire à la fois. Dans le cas où vous voulez montrer la liste utiliser la pagination (.Skip() and .prendre() )

3
répondu Sr.PEDRO 2013-03-18 14:37:57

alors que le GC compacte le tas de petits objets dans le cadre d'une stratégie d'optimisation visant à éliminer les trous de mémoire, le GC ne compacte jamais le tas de gros objets pour des raisons de rendement**(le coût du compactage est trop élevé pour les gros objets (plus de 85KO en taille))**. Par conséquent, si vous exécutez un programme qui utilise de nombreux objets de grande taille dans un système x86, vous pourriez rencontrer des exceptions OutOfMemory. Si vous exécutez ce programme dans un système x64, vous pourriez avoir la fragmentation du tas.

3
répondu Ali Bayat 2018-07-02 03:23:26

je sais que c'est une vieille question, mais depuis aucune réponse mentionné le tas d'objets volumineux, cela peut être utile à d'autres personnes qui trouvent cette question ...

toute attribution de mémoire dans .NET qui est supérieure à 85 000 octets provient du tas de gros objets (LOH) et non du tas normal de petits objets. Pourquoi est-ce important? Parce que le gros tas d'objets n'est pas compacté. Ce qui signifie que le gros tas d'objets devient fragmenté et dans mon expérience, cela conduit inévitablement à des erreurs de mémoire.

dans la question initiale, la liste contient 50 000 éléments. À l'interne, une liste utilise un tableau, et en supposant que 32 bits nécessitent 50 000 x 4 octets = 200 000 octets (ou le double si 64 bits). Ainsi, l'allocation de mémoire provient du tas d'objets volumineux.

alors que pouvez-vous faire?

Si vous utilisez un .net version antérieure à la version 4.5.1 alors tout ce que vous pouvez faire est d'être conscient du problème et essayer pour l'éviter. Donc, dans ce cas-ci, au lieu d'avoir une liste de véhicules, vous pourriez avoir une liste de véhicules, pourvu qu'aucune liste n'ait jamais eu plus de 18 000 éléments en elle. Qui peut mener à laide code, mais c'est viable contourner.

si vous utilisez .net 4.5.1 ou plus tard, le comportement du collecteur d'ordures a changé subtilement. Si vous ajoutez la ligne suivante où vous êtes sur le point de faire de grandes allocations de mémoire:

System.Runtime.GCSettings.LargeObjectHeapCompactionMode = System.Runtime.GCLargeObjectHeapCompactionMode.CompactOnce;

cela forcera le ramasseur d'ordures à compacter le tas d'objets de grande taille - la prochaine fois seulement.

ce n'est peut-être pas la meilleure solution, mais ce qui suit a fonctionné pour moi:

int tries = 0;
while (tries++ < 2)
{
  try 
  {
    . . some large allocation . .
    return;
  }
  catch (System.OutOfMemoryException)
  {
    System.Runtime.GCSettings.LargeObjectHeapCompactionMode = System.Runtime.GCLargeObjectHeapCompactionMode.CompactOnce;
    GC.Collect();
  }
}

bien sûr, cela n'aide que si vous avez la mémoire physique (ou virtuelle) disponible.

0
répondu Brian Cryer 2018-04-27 13:28:03

au fur et à mesure que .Net progresse, il semble que leur capacité à ajouter de nouvelles configurations 32 bits fasse voyager tout le monde.

si vous êtes sur .net Framework 4.7.2 faire ce qui suit:

Allez à Propriétés de Projet

Construire

Décochez la case "préfèrent 32 bits'

santé!

0
répondu user2326106 2018-09-15 06:56:25