OVH Cloud OVH Cloud

Optimisation mémoire

4 réponses
Avatar
Delf
Y a-t-il des méthodes pour optimiser l'espace mémoire utilisé par une
application écrite en C# ? J'ai créé un service qui consomme uniquement
un Web Service et ce dernier prend 17/18 Mo de Ram...

Merci.

--
Delf

4 réponses

Avatar
De Simone Alessandro
Delf wrote:

Y a-t-il des méthodes pour optimiser l'espace mémoire utilisé par une
application écrite en C# ?




Déjà il faut bien penser ton app pour quelle ne garde pas des références
vers des objets dont tu n'as plus besoin, sinon le garbage collector ne
pourra pas les collecter même si tu ne les utilises plus.

Il y a aussi des objets où tu dois appeler explicitement Dispose() pour
relâcher les ressources associées à ces objets (par exemple un timer ou
des resources unmanaged).

Si vraiment c'est nécessaire, tu peux forcer le garbage collector à des
endroits stratégiques de ton app avec System.GC.Collect() mais c'est
déconseillé et ça peut être lent selon le travail du GC.


> un Web Service et ce dernier prend 17/18 Mo de Ram...

Si ça peut te rassurer, on est plusieurs a avoir remarqué que une petite
app en dotnet prend environs 20mb de ram et garde globalement cet usage
de la mémoire même quand l'application devient plus complexe.
C'est le garbage collector qui décide quand il est temps de nettoyer la
mémoire, les critères utilisés sont connu par Microsoft ;-)
Mais les objets nouvellement créés ont plus de chance d'être la cible du
garbage collector que les anciens objets. Vu que les anciens objets ont
réussi à survivre parce qu'ils sont (ou étaient longtemps) référencés
(ou par chance). Donc il se peut que le GC ne collecte pas avant
longtemps certains vieux objets. Cette notion de "vieillesse" d'un objet
est matérialisée par un numéro de génération. Donc tu peux forcer le
garbage collector sur des objets d'une génération en particulier :

Collect(numéro_de_génération) // numéro compris entre 0 et MaxGeneration

Tu peux aussi utiliser GetGeneration(ton_objet) pour savoir l'age d'un
objet en particulier.

Mais en toute logique si ton application prend 20 mb de ram mais qu'elle
utilise en réalité moins et que la mémoire libre du système diminue
fortement, alors le garbage collector devrait s'activer. Donc en gros
ton application peut prendre 20mb de mémoire temps que ça ne pose pas
problème de ram. Je pense que ce qui poserait plus de problèmes, c'est
surtout d'avoir un garbage collector qui s'active trop souvent car il
met en pause toute l'application pendant ce processus et cette pause
peut devenir détectable par l’utilisateur.





--
Alessandro De Simone
email : (remove ".IHATESPAM")

Win32
Avatar
Delf
De Simone Alessandro wrote:

Déjà il faut bien penser ton app pour quelle ne garde pas des références
vers des objets dont tu n'as plus besoin, sinon le garbage collector ne
pourra pas les collecter même si tu ne les utilises plus.



On m'a toujours dit que c'était le GC qui s'occupait de la destruction
des objets. Dans quels cas est-ce au développeur de les détruire ?

Il y a aussi des objets où tu dois appeler explicitement Dispose() pour
relâcher les ressources associées à ces objets (par exemple un timer ou
des resources unmanaged).



Bon à savoir. Est-ce précisé dans le MSDN systématiquement ?

Si vraiment c'est nécessaire, tu peux forcer le garbage collector à des
endroits stratégiques de ton app avec System.GC.Collect() mais c'est
déconseillé et ça peut être lent selon le travail du GC.



Je ne préfère pas y toucher :)

Si ça peut te rassurer, on est plusieurs a avoir remarqué que une petite
app en dotnet prend environs 20mb de ram et garde globalement cet usage
de la mémoire même quand l'application devient plus complexe.



Dans mon cas, il n'y a même pas d'interface graphique vu qu'il s'agit
d'un service. 5 services .NET de ce genre, c'est déjà 100Mo...

[...]
Mais en toute logique si ton application prend 20 mb de ram mais qu'elle
utilise en réalité moins et que la mémoire libre du système diminue
fortement, alors le garbage collector devrait s'activer. Donc en gros
ton application peut prendre 20mb de mémoire temps que ça ne pose pas
problème de ram.



Ok.

Je pense que ce qui poserait plus de problèmes, c'est
surtout d'avoir un garbage collector qui s'active trop souvent car il
met en pause toute l'application pendant ce processus et cette pause
peut devenir détectable par l’utilisateur.



Le GC n'est pas sur son propre thread ? Pourquoi bloquerait-il
l'application.
Merci pour ces précisions.

--
Delf
Avatar
De Simone Alessandro
Delf a écrit :
De Simone Alessandro wrote:

Déjà il faut bien penser ton app pour quelle ne garde pas des
références vers des objets dont tu n'as plus besoin, sinon le garbage
collector ne pourra pas les collecter même si tu ne les utilises plus.




On m'a toujours dit que c'était le GC qui s'occupait de la destruction
des objets. Dans quels cas est-ce au développeur de les détruire ?



Quand je dis "ne pas garder les références" c'est par exexemple avoir
une classe ou tu fais toute une série de new et que après un certain
temps une partie des objets référencés dans ta classe sont inutiles. Tu
dois alors enlever les références en faisant par exemple nom_variable =
null;
Donc éviter de faire trop de références dans tous les sens est une bonne
idée.

Et c'est effectivement le garbage collector qui se charge de tout mais
tu as aussi des optimisations possibles. Sur MSDN il y a un article
(dont je n'ai plus le lien) qui donne des conseils.


Il y a aussi des objets où tu dois appeler explicitement Dispose()
pour relâcher les ressources associées à ces objets (par exemple un
timer ou des resources unmanaged).




Bon à savoir. Est-ce précisé dans le MSDN systématiquement ?



Quand tu vois qu'un objet implémente la fonction Dispose, cela veut dire
que tu peux explicitement libérer des resources sans attendre que le
garbage collector passe. Il suffit de voir dans MSDN la liste des
membres de la classe concernée.
Tu as cette fonction pour tous les objets qui détiennent des ressources
qui ne sont pas managées par le runtime. Par exemple les contrôles, les
objets gdi, ...


Si vraiment c'est nécessaire, tu peux forcer le garbage collector à
des endroits stratégiques de ton app avec System.GC.Collect() mais
c'est déconseillé et ça peut être lent selon le travail du GC.








Si ça peut te rassurer, on est plusieurs a avoir remarqué que une
petite app en dotnet prend environs 20mb de ram et garde globalement
cet usage de la mémoire même quand l'application devient plus complexe.




Dans mon cas, il n'y a même pas d'interface graphique vu qu'il s'agit
d'un service. 5 services .NET de ce genre, c'est déjà 100Mo...



oui mais je dirais que le GC a été inventé surtout pour permettre de
programmer facilement des systèmes très complexes et éviter des mémory
leak qui font planter le programme après quelques semaines d'utilisation.

Certes ca à l'air d'utiliser plus de mémoire mais les GC modernes (comme
dotnet) sont tout aussi rapide qu'une gestion manuelle malloc/free.

[...]
Mais en toute logique si ton application prend 20 mb de ram mais
qu'elle utilise en réalité moins et que la mémoire libre du système
diminue fortement, alors le garbage collector devrait s'activer. Donc
en gros ton application peut prendre 20mb de mémoire temps que ça ne
pose pas problème de ram.




Ok.

Je pense que ce qui poserait plus de problèmes, c'est surtout d'avoir
un garbage collector qui s'active trop souvent car il met en pause
toute l'application pendant ce processus et cette pause peut devenir
détectable par l’utilisateur.




Le GC n'est pas sur son propre thread ? Pourquoi bloquerait-il
l'application.
Merci pour ces précisions.

--
Delf



(je suis certain que le thread est bloqué en .NET mais je ne suis pas
certain à 100% de l'explication)

- Le GC effectue aussi une sorte de défragmentation dans le heap. C'est
à dire qu'après avoir fait le recensement de tous les objets encore
utilisés, ils bougent ceux-ci au début du heap, laissant tout l'espace
libre à la fin. Il n'y a donc pas de 'trous' dans les données et le
garbage collector sait exactement ou se terminent ces données et donc
l'allocation de mémoire est très rapide.

Donc durant cette défragmentation, comme les objets changent de position
mémoire, le thread de l'application doit être mise en pause pour changer
les pointeurs vers les nouvelles positions mémoires. Il est également
possible que le pointeur vers l'objet soit directement dans le registre
du processeur (l'image du registre en réalité).
(La défragmentation étant très lente c'est pour cela qu'il y a les
générations)

- Peut être aussi parce que pour savoir quels objets sont utilisés, le
GC crée un graphe. Il ne faudrait donc pas que durant cette opération
des objets changent d'état.

Voila j'espère ne pas avoir dit une bêtise sur la fin :-)

remarque : le thread peut ne pas être mis en pause s'il execute du code
non managed. Dans ce cas il continue sont éxécution jusqu'au retour en
mode managed.


--
Alessandro De Simone
email : (remove ".IHATESPAM")

Win32
Avatar
Guillaume Davion
Déjà, le lien sur l'article à propos de la gestion de la mémoire :
http://msdn.microsoft.com/msdnmag/issues/1100/GCI/

Ensuite, ce n'est pas parce que avec un webservice on utilise 20Mo de
ram qu'on va en utiliser 100 pour 5 services. Le garbage collector ne
voit peut être pas l'intérêt de démarer pour récupérer quelques
mégas, mais le verrait peut-être sur une application plus lourde.

Enfin, le critère pour savoir si on doit utiliser la méthode Dispose
est de savoir si la classe (ou une des classes dans son ascendance)
implémente l'interface IDisposable. Et l'idéal, si on est en C#, et
d'utiliser la syntaxe using maVariableDisposable. Comme ça, l'objet
sera automatiquement "disposé" à la fin du bloc.