web-dev-qa-db-fra.com

Pourquoi est-ce que j'obtiens une exception de mémoire insuffisante dans mon application C #?

Ma mémoire est physique 4G, mais pourquoi je suis sorti de l'exception de mémoire même si je crée juste un objet mémoire 1.5G. Des idées pourquoi? (J'ai vu en même temps, dans l'onglet performances du gestionnaire de tâches, la mémoire n'est pas entièrement occupée, et je pourrais également taper ici - donc la mémoire n'est pas réellement faible, donc je pense que j'ai rencontré d'autres limitations de mémoire)?

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace TestBigMemoryv1
{
    class MemoryHolderFoo
    {
        static Random seed = new Random();
        public Int32 holder1;
        public Int32 holder2;
        public Int64 holder3;

        public MemoryHolderFoo()
        {
            // prevent from optimized out
            holder1 = (Int32)seed.NextDouble();
            holder2 = (Int32)seed.NextDouble();
            holder3 = (Int64)seed.NextDouble();
        }
    }

    class Program
    {
        static int MemoryThreshold = 1500; //M
        static void Main(string[] args)
        {
            int persize = 16;
            int number = MemoryThreshold * 1000 * 1000/ persize;
            MemoryHolderFoo[] pool = new MemoryHolderFoo[number];
            for (int i = 0; i < number; i++)
            {
                pool[i] = new MemoryHolderFoo();
                if (i % 10000 == 0)
                {
                    Console.Write(".");
                }
            }

            return;
        }
    }
}
29
George2

Dans une application Windows 32 bits normale, le processus ne dispose que de 2 Go de mémoire adressable. Cela n'a aucun rapport avec la quantité de mémoire physique disponible.

Donc 2 Go disponibles mais 1,5 est le maximum que vous pouvez allouer. La clé est que votre code n'est pas le seul code en cours d'exécution dans le processus. L'autre 0,5 Go est probablement la fragmentation CLR plus dans le processus.

Mise à jour: dans .Net 4.5 dans un processus 64 bits, vous pouvez avoir de grands tableaux si le paramètre gcAllowVeryLargeObjects est activé:

Sur les plates-formes 64 bits, active les baies dont la taille totale est supérieure à 2 gigaoctets (Go). Le nombre maximal d'éléments dans un tableau est UInt32.MaxValue.

<configuration>
  <runtime>
    <gcAllowVeryLargeObjects enabled="true" />
  </runtime>
</configuration>
40
JaredPar

Juste en plus des autres points; si vous voulez accéder à une quantité sale de mémoire, pensez à x64 - mais sachez que la taille maximale d'un seul objet est toujours de 2 Go. Et parce que les références sont plus grandes en x64, cela signifie que vous obtenez réellement une taille de tableau/liste maximale plus petite pour les types de référence. Bien sûr, au moment où vous atteignez cette limite, vous vous trompez probablement de toute façon!

Autres options:

  • utiliser des fichiers
  • utiliser une base de données

(évidemment, les deux ont une différence de performances par rapport à la mémoire en cours)


Mise à jour: dans les versions de .NET antérieures à 4.5, la taille maximale de l'objet est de 2 Go. À partir de la version 4.5, vous pouvez allouer des objets plus grands si gcAllowVeryLargeObjects est activé. Notez que la limite de string n'est pas affectée, mais les "tableaux" devraient également couvrir les "listes", car les listes sont soutenues par des tableaux.

10
Marc Gravell

Juste pour ajouter aux réponses précédentes: vous pouvez aller au-delà de la limite de 2 Go sur les systèmes démarrés avec les drapeaux de démarrage/3Gb [et éventuellement userva].

5
KristoferA

Vérifiez que vous générez un processus 64 bits et non 32 bits, qui est le mode de compilation par défaut de Visual Studio. Pour ce faire, faites un clic droit sur votre projet, Propriétés -> Build -> plate-forme cible: x64. Comme tout processus 32 bits, les applications Visual Studio compilées en 32 bits ont une limite de mémoire virtuelle de 2 Go.

Chaque processus possède sa propre mémoire virtuelle, appelée espace d'adressage, dans laquelle il mappe le code qu'il exécute et les données qu'il manipule. Un processus 32 bits utilise des pointeurs d'adresse de mémoire virtuelle 32 bits, ce qui crée une limite supérieure absolue de 4 Go (2 ^ 32) pour la quantité de mémoire virtuelle qu'un processus 32 bits peut traiter. Cependant, le système d'exploitation en requiert la moitié (pour référencer son propre code et ses propres données), créant une limite de 2 Go pour chaque processus. Si votre application 32 bits essaie de consommer plus que la totalité des 2 Go de son espace d'adressage, elle renverra "System.OutOfMemory", même si la mémoire physique de votre ordinateur n'est pas pleine.

Les processus 64 bits n'ont pas cette limitation, car ils utilisent des pointeurs 64 bits, leur espace d'adressage maximal théorique est donc de 16 exaoctets (2 ^ 64). En réalité, Windows x64 limite la mémoire virtuelle des processus à 8 To. La solution au problème de limite de mémoire est alors de compiler en 64 bits.

Cependant, la taille de l'objet dans Visual Studio est toujours limitée à 2 Go, par défaut. Vous pourrez créer plusieurs tableaux dont la taille combinée sera supérieure à 2 Go, mais vous ne pouvez pas par défaut créer des tableaux de plus de 2 Go. Si tout va bien, si vous voulez toujours créer des tableaux de plus de 2 Go, vous pouvez le faire en ajoutant le code suivant à votre fichier app.config:

<configuration>
  <runtime>
    <gcAllowVeryLargeObjects enabled="true" />
  </runtime>
</configuration>
5
Shift Technology

Vous avez un maximum de 2 Go de mémoire adressable en tant qu'application 32 bits, comme les autres affiches l'ont mentionné. N'oubliez pas les frais généraux. Vous créez un tableau de 93 millions d'objets - s'il se trouve qu'il y a 4 octets de surcharge par objet, cela représente 350 Mo de mémoire supplémentaires.

4
geofftnz

Encore une chose à savoir; certains objets .NET nécessitent une mémoire "contiguë". c'est-à-dire que si vous essayez d'allouer un grand tableau, le système peut avoir besoin non seulement de suffisamment de mémoire libre dans votre processus, mais aussi pour que toute cette mémoire libre soit en un seul gros morceau ... et malheureusement la mémoire du processus se fragmente au fil du temps, donc cela peut pas disponible.

Certains objets/types de données ont cette exigence et d'autres pas ... Je ne me souviens pas lesquels le font, mais il me semble que StringBuilder et MemoryStream ont des exigences différentes.

4
Yort

Sur un système d'exploitation Windows 32 bits, la mémoire maximale en "mode utilisateur" à laquelle une seule application peut accéder est de 2 Go ... en supposant que vous disposez de 4 Go de mémoire sur la boîte.

consommation de mémoire de l'application VC++ non gérée sur le serveur Windows

http://blogs.technet.com/markrussinovich/archive/2008/07/21/3092070.aspx

(C'est drôle que vous ayez posé cette question parce que j'ai demandé presque la même chose hier ...)

3
uzbones