Réduire l'utilisation de la mémoire of.NET applications?

Quels sont quelques conseils pour réduire l'utilisation de la mémoire des applications .NET? Considérez le programme C simple suivant.

class Program
{
    static void Main(string[] args)
    {
        Console.ReadLine();
    }
}

compilé dans release mode pour x64 et en cours d'exécution à L'extérieur de Visual Studio, le Gestionnaire des tâches rapporte ce qui suit:

Working Set:          9364k
Private Working Set:  2500k
Commit Size:         17480k

C'est un peu mieux s'il est compilé juste pour x86 :

Working Set:          5888k
Private Working Set:  1280k
Commit Size:          7012k

je puis essayé le programme suivant, qui fait la même chose, mais tente de couper la taille du processus après l'initialisation de l'exécution:

class Program
{
    static void Main(string[] args)
    {
        minimizeMemory();
        Console.ReadLine();
    }

    private static void minimizeMemory()
    {
        GC.Collect(GC.MaxGeneration);
        GC.WaitForPendingFinalizers();
        SetProcessWorkingSetSize(Process.GetCurrentProcess().Handle,
            (UIntPtr) 0xFFFFFFFF, (UIntPtr)0xFFFFFFFF);
    }

    [DllImport("kernel32.dll")]
    [return: MarshalAs(UnmanagedType.Bool)]
    private static extern bool SetProcessWorkingSetSize(IntPtr process,
        UIntPtr minimumWorkingSetSize, UIntPtr maximumWorkingSetSize);
}

Les résultats sur x86 Libération en dehors de Visual Studio:

Working Set:          2300k
Private Working Set:   964k
Commit Size:          8408k

ce qui est un peu mieux, mais cela semble quand même excessif pour un programme aussi simple. Y a-t-il des astuces pour rendre un processus C# Un peu plus léger? J'écris un programme qui est conçu pour fonctionner en arrière-plan la plupart du temps. Je fais déjà n'importe quel truc d'interface utilisateur dans un Application Domain ce qui signifie que le truc d'interface utilisateur peut être déchargé en toute sécurité, mais prendre 10 Mo quand il est juste assis dans l'arrière-plan semble excessif.

P.S. quant à savoir pourquoi je m'en soucierais - - les utilisateurs (de courant)ont tendance à s'inquiéter de ces choses. Même si cela n'a presque aucun effet sur la performance, les utilisateurs semi-tech-savvy (mon public cible) ont tendance à aller dans hissy correspond à l'utilisation de la mémoire de l'application d'arrière-plan. Même moi je flippe quand je vois Adobe Updater prendre 11 Mo de mémoire et se sentir apaisé par la touche calmante de Foobar2000, qui peut prendre moins de 6 Mo même en jouant. Je sais que dans les systèmes d'exploitation modernes, ces trucs n'ont pas vraiment d'importance techniquement, mais ça ne veut pas dire qu'ils n'ont pas d'effet sur la perception.

104
demandé sur Peter Mortensen 2009-08-27 23:40:57

9 réponses

  1. vous pouvez vérifier la question de débordement de la pile .net EXE memory footprint .
  2. La MSDN blog Travailler ensemble != empreinte mémoire réelle est tout au sujet de démystifier l'ensemble de travail, la mémoire de processus et la façon d'effectuer des calculs exacts sur votre consommation totale en mémoire vive.

Je ne dirai pas que vous devriez ignorer l'empreinte mémoire de votre application -- évidemment, plus petit et plus efficace ne tendent à être souhaitable. Toutefois, vous devriez considérer ce que vos besoins réels.

si vous écrivez des formulaires Windows standard et des applications client WPF qui sont destinés à fonctionner sur le PC d'une personne, et est susceptible d'être l'application principale dans laquelle l'utilisateur opère, vous pouvez vous en tirer avec être plus lackadaisical sur l'allocation de mémoire. (Aussi longtemps que tout cela s' désalloué.)

Cependant, pour s'adresser à certaines personnes ici qui disent de ne pas s'inquiéter à ce sujet: si vous écrivez une application de formulaires Windows qui sera en cours d'exécution dans un environnement de services de terminal, sur un serveur partagé éventuellement utilisé par 10, 20 ou plus Utilisateurs, alors oui, vous devez absolument considérer l'utilisation de la mémoire. Et vous devrez être vigilant. La meilleure façon de régler ce problème consiste à concevoir une bonne structure de données et à suivre les pratiques exemplaires concernant le moment et les ressources allouées.

32
répondu John Rudy 2017-05-23 12:00:25

. les applications réseau auront une plus grande empreinte par rapport aux applications natives en raison du fait qu'elles doivent toutes deux charger l'exécution et l'application dans le processus. Si vous voulez quelque chose de vraiment rangé, .NET peut ne pas être la meilleure option.

Cependant, gardez à l'esprit que si votre application est la plupart du temps endormie, les pages mémoire nécessaires seront échangées de mémoire et donc pas vraiment être beaucoup d'un fardeau sur le système dans son ensemble la plupart du temps.

si vous voulez garder l'empreinte petite, vous devrez penser à l'utilisation de la mémoire. Voici quelques idées:

  • réduisez le nombre d'objets et assurez-vous de ne pas tenir plus longtemps que nécessaire.
  • être conscient de List<T> et des types similaires qui doublent la capacité en cas de besoin, car ils peuvent conduire à une hausse de 50% des déchets.
  • vous pourriez envisager d'utiliser les types de valeur par rapport aux types de référence pour forcer plus de mémoire sur la pile, mais gardez à l'esprit que l'espace de pile par défaut n'est que de 1 Mo.
  • éviter les objets de plus de 85000 octets, car ils iront à LOH qui n'est pas compacté et donc peut facilement se fragmenter.

ce n'est probablement pas une liste exhaustive, mais juste quelques idées.

43
répondu Brian Rasmussen 2009-10-25 12:28:57

une chose que vous devez considérer dans ce cas est le coût de la mémoire du CLR. Le CLR est chargé pour chaque processus .Net et donc des facteurs dans les considérations de mémoire. Pour un programme aussi simple / petit, le coût du CLR va dominer votre empreinte mémoire.

Il serait beaucoup plus intéressant de construire une application réelle et d'afficher le coût par rapport au coût de ce programme de base.

16
répondu JaredPar 2009-08-27 19:45:01

aucune suggestion spécifique en soi, mais vous pouvez jeter un oeil au CLR Profiler (téléchargement gratuit de Microsoft).

Une fois que vous l'avez installé, regardez ce how-to page .

dans le how-to:

Cette Comment vous montre comment utiliser le Outil de profileur CLR pour étudier votre allocation de mémoire de l'application profil. Vous pouvez utiliser CLR Profiler pour identifier le code qui provoque la mémoire problèmes, tels que les fuites de mémoire et ordures excessives ou inefficaces collection.

7
répondu Donut 2009-08-27 19:43:53

pourrait vouloir examiner l'utilisation de la mémoire d'une application" réelle".

similaire à Java il y a une certaine quantité fixe de frais généraux pour l'exécution indépendamment de la taille du programme, mais la consommation de mémoire sera beaucoup plus raisonnable après ce point.

6
répondu Eric Petroelje 2009-08-27 19:46:15

il y a encore des moyens de réduire l'ensemble de travail privé de ce programme simple:

  1. NGEN votre demande. Cela supprime le coût de compilation JIT de votre processus.

  2. de Train de votre application en utilisant MPGO la réduction de l'utilisation de la mémoire et puis NGEN.

4
répondu Feng Yuan 2014-04-25 06:06:51

il y a plusieurs façons de réduire votre empreinte.

une chose que vous aurez toujours à vivre avec dans .NET est que la taille de l'image native de votre code IL est énorme

et ce code ne peut pas être complètement partagé entre les instances de la demande. Même les ensembles NGEN'ed ne sont pas complètement statiques, ils ont encore quelques petites pièces qui doivent être croustillantes.

les gens ont aussi tendance à écrire du code qui bloque la mémoire bien plus longtemps que nécessaire.

un exemple souvent vu: prendre un lecteur de données, charger le contenu dans un DataTable juste pour l'écrire dans un fichier XML. Vous pouvez facilement tomber sur un OutOfMemoryException. OTOH, vous pouvez utiliser un XmlTextWriter et faire défiler le lecteur de données, en émettant des XmlNodes pendant que vous faites défiler le curseur de la base de données. De cette façon, vous n'avez que l'enregistrement de la base de données actuelle et sa sortie XML en mémoire. Qui n'obtiendront jamais (ou peu susceptibles d'obtenir) une génération de collecte des ordures plus élevée et peuvent donc être réutilisés.

la même chose s'applique à obtenir une liste de quelques instances, à faire des choses (qui produisent des milliers de nouvelles instances, qui peuvent rester référencées quelque part), et même si vous n'en avez pas besoin par la suite, vous référencez tout jusqu'après l'avant-plan. Explicitement null-ing votre liste d'entrées et vos sous-produits temporaires signifie, cette mémoire peut être réutilisée même avant de quitter la boucle.

C # a une excellente caractéristique appelée itérateurs. Ils vous permettent de streamer des objets en faisant défiler votre entrée et de ne garder l'instance courante que jusqu'à ce que vous obteniez la suivante. Même en utilisant LINQ, vous n'avez pas besoin de tout garder autour de vous juste parce que vous vouliez qu'il soit filtré.

2
répondu Robert Giesecke 2011-08-16 07:52:01

abordant la question générale du titre et non la question spécifique:

si vous utilisez un composant COM qui renvoie beaucoup de données (par exemple, grands réseaux de doubles 2xN) et seulement une petite fraction est nécessaire alors on peut écrire un composant wrapper COM qui cache la mémoire de .NET et ne renvoie que les données qui est nécessaire.

Qu'est ce que j'ai fait dans mon application principale et il amélioration significative de la consommation de mémoire.

1
répondu Peter Mortensen 2009-08-27 20:47:14

j'ai constaté que l'utilisation des APIs Setrocessworkingsetsize ou EmptyWorkingSet pour forcer les pages de mémoire au disque périodiquement dans un processus long en cours peut conduire à toute la mémoire physique disponible sur une machine à disparaître effectivement jusqu'à ce que la machine est redémarrée. Nous avons chargé une DLL. net dans un processus natif qui utiliserait L'API EmptyWorkingSet (une alternative à L'utilisation de SetProcessWorkingSetSize) pour réduire l'ensemble de travail après avoir effectué une tâche intensive en mémoire. J'ai trouvé qu'après entre un jour et une semaine, une machine affiche 99% d'utilisation de mémoire physique dans le Gestionnaire de tâches, alors qu'aucun processus n'utilise de mémoire significative. Peu de temps après, la machine ne réagissait plus, ce qui nécessitait un redémarrage dur. Ces machines étaient plus de deux douzaines de serveurs Windows Server 2008 R2 et 2012 R2 fonctionnant à la fois sur le matériel physique et virtuel.

peut-être que le fait d'avoir chargé le code .NET dans un processus natif avait quelque chose à voir avec cela, mais utiliser EmptyWorkingSet (ou SetProcessWorkingSetSize) à vos risques et périls. Peut-être ne l'utilisez une fois après le lancement initial de votre application. J'ai décidé de désactiver le code et de laisser le collecteur d'ordures gérer seul l'utilisation de la mémoire.

0
répondu Stuart Welch 2015-02-18 09:34:18