Nous developpons plusieurs applications de type serveur avec un GUI
tres peu riche et elles prennent vraiment beaucoup plus de RAM que si
nous avions developpe ces memes applis dans un langage Win32.
Chaque appli prend environ 50 MB de RAM + 50MB de memoire virtuelle.
Nous avons 5 applis => 250 MB de RAM + 250 MB de RAM virtuelle !!!
Si nous laissons tourner ces applications sans faire appel au
GC.Collect() certaines de ces applis prennent plus de 200MB de RAM et
autant de memoire virtuelle et on atteint rapidement plus de 600 MB de
RAM et autant de memoires virtuelles.
Ces applications doivent tourner en meme temps sur un serveur central
sur lequel SQL Serveur est aussi installe.
Nous avons note que le fait de laisser le GC gerer la memoire affecte
les performances de SQL serveur car il a tout simplement moins de RAM
et de memoire virtuelle.
Nous avons donc conclu qu'il fallait faire appel au GC.Collect au moins
une fois toutes les minutes dans nos applis clientes (meme probleme sur
machine cliente) et serveurs.
Avez-vous ce meme genre de probleme ?
Merci,
Michael
--
Michael
----
http://michael.moreno.free.fr/
http://port.cogolin.free.fr/
"Patrice Manac'h" a écrit dans le message de news: uI%23K%
Bonjour,
tout dépend de la portée de la vairable.Mettre à null peut servir.
C'est vrai mais j'ai trop souvent vu du code du genre:
void MaFonction() { MaRessource res = new MaRessource(...); // faire des choses avec res res.Dispose(); res = null; // ne sert strictement à rien, sinon à ralentir le prog }
Quand au Dispose, vous ne devez pas supposer que Dispose l'appelle implicitement.
Dans la plupart des cas, je n'utilise ni Dispose, ni Close (je suppose que c'est de Close que tu voulais parler), mais using. L'exemple précédent devient:
void MaFonction() { using (MaRessource res = new MaRessource(...)) { // faire des choses avec res } }
C'est plus propre, et surtout on a la garantie que Dispose() sera appelé si le block lance une exception.
Bruno
Cordialement,
P. Manac'h MCS France
"Bruno Jouhier" a écrit dans le message de news: 43d5d60e$0$26396$
"Patrice Manac'h" a écrit dans le message de news:
Bonjour,
le GC collecte par défaut tout ce qui est libéré mais quelques astuces permettent de lui simplifier la vie. La plus courant est pour tous les objects IDisposable de faire explicitement lorsqu'ils ne sont plus utilisés : - un Close si c'est disponible. - un Dispose. - et de mettre la variable à null.
La seule chose à appeler c'est Dispose. Mettre la variable à null ne sert en général à rien. En C#, il est conseillé d'utiliser la construction "using" (voir la doc).
C'est simple mais cela peut très vite payer. Pour le comprendre, il faut regarder comment le GC fonctionne. Par défaut, il gère 3 zones mémoires dont la taille par processeur est de l'ordre de : - quelques dizaines de Ko pour les objets les plus récents. - quelques Mo pour les objets en génération 1. - plus de 10 Mo pour les objets en génération 2.
Lorsque le système manque de mémoire (besoin d'allocation, génération 0, 1 ou 2 pleine, etc), le GC créé pour chaque génération un graphe de dépendance de manière à déterminer quels objets peuvent être virer. Si un objet est IDisposable mais pas disposé, le GC le Dispose et le monte en génération supérieure. Lors d'un prochain GC, il sera viré. Mais si le Dispose avait été fait à la main, il aurait été viré tout de suite.
Le GC ne construit pas de graphes de dépendances (comment les maintiendrait-il à jour en permanence?) et il ne regarde pas si les objets implémentent IDisposable. En revanche, il s'intéresse aux finaliseurs (destructeurs).
Le rôle de IDisposable, ce n'est pas de libérer la mémoire des objets .NET plus tôt, c'est de libérer les ressources "non managées" référencées par des objets .NET. En lisant ce qui précède, on a l'impression qu'on peut améliorer les choses en mettant des interfaces IDisposable et des appels à Dispose partout, ce qui serait une grosse erreur. IDisposable et Dispose n'ont de sens que si les objets .NET en question référencent (directement ou indirectement) des ressources "non managées". Et l'appel à Dispose ne fait que libérer la ressource non managée et empêcher que l'objet .NET passe dans la queue de finalisation (si l'objet référence "directement" une ressource non managée, il est de bon ton qu'il ait un finaliseur, et Dispose doit inhiber le finaliseur dans ce cas). Dispose n'a aucun impact sur la façon dont le GC gère les générations d'objets.
Le résultat est que les générations 1 et 2 se remplissent et que vu la taille de ces blocs, la construction du graphe devient de plus en plus couteuse.
Non, le GC ne fonctionne pas comme ça.
Essayer de travailler ce point de prime abord. Des dumps mémoire (windgb, .Net Profiler) pourraient aussi permettre de savoir si les ressources sont bien libérées.
Cordialement,
Patrice
"Michael Moreno" a écrit dans le message de news:
Bonjour,
Il n'est pas recommandé d'utiliser directement la méthode GC.Collect()... la CLR s'occupant de nettoyer la mémoire automatiquement au moment le plus opportun pour ne pas ralentir l'exécution de l'application.
Certes mais ca ne le fait pas, le disque swappe a tout va et ca ralentit SQL Server (BDD > 200 GB). Il s'agit d'une appli financiere temps reel et les CPU sont a 50% du temps a pleine charge ou pas loin (i.e > 90%).
Si le GC ne nettoie pas la mémoire, c'est qu'il estime que le RAM ne vient pas à manquer sur le système. Combien de RAM disposez-vous sur votre serveur ?
Serveurs de test 1 et 2 GB. Chez nos clients on a en pratique 1 ou 2 GB. On a un seul cas extreme avec 512 GB. En moyenne l'appel a GC.Collect() prend 3,7 ms toutes les minutes ce qui est tres peu donc le CLR devrait le faire tout seul sauf si bien sur il ne le fait que quand l'appli est Idle ou que la charge du CPU est < 20% ce qui n'arrive jamais.
Merci.
-- Michael ---- http://michael.moreno.free.fr/ http://port.cogolin.free.fr/
"Patrice Manac'h" <patmanac@online.microsoft.com> a écrit dans le message de
news: uI%23K%238QIGHA.984@tk2msftngp13.phx.gbl...
Bonjour,
tout dépend de la portée de la vairable.Mettre à null peut servir.
C'est vrai mais j'ai trop souvent vu du code du genre:
void MaFonction()
{
MaRessource res = new MaRessource(...);
// faire des choses avec res
res.Dispose();
res = null; // ne sert strictement à rien, sinon à ralentir le prog
}
Quand au Dispose, vous ne devez pas supposer que Dispose l'appelle
implicitement.
Dans la plupart des cas, je n'utilise ni Dispose, ni Close (je suppose que
c'est de Close que tu voulais parler), mais using. L'exemple précédent
devient:
void MaFonction()
{
using (MaRessource res = new MaRessource(...))
{
// faire des choses avec res
}
}
C'est plus propre, et surtout on a la garantie que Dispose() sera appelé si
le block lance une exception.
Bruno
Cordialement,
P. Manac'h
MCS France
"Bruno Jouhier" <bjouhier@club-internet.fr> a écrit dans le message de
news: 43d5d60e$0$26396$7a628cd7@news.club-internet.fr...
"Patrice Manac'h" <patmanac@online.microsoft.com> a écrit dans le message
de news: ezejNd2HGHA.1676@TK2MSFTNGP09.phx.gbl...
Bonjour,
le GC collecte par défaut tout ce qui est libéré mais quelques astuces
permettent de lui simplifier la vie. La plus courant est pour tous les
objects IDisposable de faire explicitement lorsqu'ils ne sont plus
utilisés :
- un Close si c'est disponible.
- un Dispose.
- et de mettre la variable à null.
La seule chose à appeler c'est Dispose. Mettre la variable à null ne sert
en général à rien.
En C#, il est conseillé d'utiliser la construction "using" (voir la doc).
C'est simple mais cela peut très vite payer. Pour le comprendre, il faut
regarder comment le GC fonctionne. Par défaut, il gère 3 zones mémoires
dont la taille par processeur est de l'ordre de :
- quelques dizaines de Ko pour les objets les plus récents.
- quelques Mo pour les objets en génération 1.
- plus de 10 Mo pour les objets en génération 2.
Lorsque le système manque de mémoire (besoin d'allocation, génération 0,
1 ou 2 pleine, etc), le GC créé pour chaque génération un graphe de
dépendance de manière à déterminer quels objets peuvent être virer. Si
un objet est IDisposable mais pas disposé, le GC le Dispose et le monte
en génération supérieure. Lors d'un prochain GC, il sera viré. Mais si
le Dispose avait été fait à la main, il aurait été viré tout de suite.
Le GC ne construit pas de graphes de dépendances (comment les
maintiendrait-il à jour en permanence?) et il ne regarde pas si les
objets implémentent IDisposable. En revanche, il s'intéresse aux
finaliseurs (destructeurs).
Le rôle de IDisposable, ce n'est pas de libérer la mémoire des objets
.NET plus tôt, c'est de libérer les ressources "non managées" référencées
par des objets .NET.
En lisant ce qui précède, on a l'impression qu'on peut améliorer les
choses en mettant des interfaces IDisposable et des appels à Dispose
partout, ce qui serait une grosse erreur.
IDisposable et Dispose n'ont de sens que si les objets .NET en question
référencent (directement ou indirectement) des ressources "non managées".
Et l'appel à Dispose ne fait que libérer la ressource non managée et
empêcher que l'objet .NET passe dans la queue de finalisation (si l'objet
référence "directement" une ressource non managée, il est de bon ton
qu'il ait un finaliseur, et Dispose doit inhiber le finaliseur dans ce
cas). Dispose n'a aucun impact sur la façon dont le GC gère les
générations d'objets.
Le résultat est que les générations 1 et 2 se remplissent et que vu la
taille de ces blocs, la construction du graphe devient de plus en plus
couteuse.
Non, le GC ne fonctionne pas comme ça.
Essayer de travailler ce point de prime abord. Des dumps mémoire
(windgb, .Net Profiler) pourraient aussi permettre de savoir si les
ressources sont bien libérées.
Cordialement,
Patrice
"Michael Moreno" <michael.ToRemove.moreno@free.fr> a écrit dans le
message de news: mn.a2c57d61f304c342.21643@free.fr...
Bonjour,
Il n'est pas recommandé d'utiliser directement la méthode
GC.Collect()... la CLR s'occupant de nettoyer la mémoire
automatiquement au moment le plus opportun pour ne pas ralentir
l'exécution de l'application.
Certes mais ca ne le fait pas, le disque swappe a tout va et ca
ralentit SQL Server (BDD > 200 GB).
Il s'agit d'une appli financiere temps reel et les CPU sont a 50% du
temps a pleine charge ou pas loin (i.e > 90%).
Si le GC ne nettoie pas la mémoire, c'est qu'il estime que le RAM ne
vient pas à manquer sur le système. Combien de RAM disposez-vous sur
votre serveur ?
Serveurs de test 1 et 2 GB. Chez nos clients on a en pratique 1 ou 2
GB. On a un seul cas extreme avec 512 GB.
En moyenne l'appel a GC.Collect() prend 3,7 ms toutes les minutes ce
qui est tres peu donc le CLR devrait le faire tout seul sauf si bien
sur il ne le fait que quand l'appli est Idle ou que la charge du CPU
est < 20% ce qui n'arrive jamais.
Merci.
--
Michael
----
http://michael.moreno.free.fr/
http://port.cogolin.free.fr/
"Patrice Manac'h" a écrit dans le message de news: uI%23K%
Bonjour,
tout dépend de la portée de la vairable.Mettre à null peut servir.
C'est vrai mais j'ai trop souvent vu du code du genre:
void MaFonction() { MaRessource res = new MaRessource(...); // faire des choses avec res res.Dispose(); res = null; // ne sert strictement à rien, sinon à ralentir le prog }
Quand au Dispose, vous ne devez pas supposer que Dispose l'appelle implicitement.
Dans la plupart des cas, je n'utilise ni Dispose, ni Close (je suppose que c'est de Close que tu voulais parler), mais using. L'exemple précédent devient:
void MaFonction() { using (MaRessource res = new MaRessource(...)) { // faire des choses avec res } }
C'est plus propre, et surtout on a la garantie que Dispose() sera appelé si le block lance une exception.
Bruno
Cordialement,
P. Manac'h MCS France
"Bruno Jouhier" a écrit dans le message de news: 43d5d60e$0$26396$
"Patrice Manac'h" a écrit dans le message de news:
Bonjour,
le GC collecte par défaut tout ce qui est libéré mais quelques astuces permettent de lui simplifier la vie. La plus courant est pour tous les objects IDisposable de faire explicitement lorsqu'ils ne sont plus utilisés : - un Close si c'est disponible. - un Dispose. - et de mettre la variable à null.
La seule chose à appeler c'est Dispose. Mettre la variable à null ne sert en général à rien. En C#, il est conseillé d'utiliser la construction "using" (voir la doc).
C'est simple mais cela peut très vite payer. Pour le comprendre, il faut regarder comment le GC fonctionne. Par défaut, il gère 3 zones mémoires dont la taille par processeur est de l'ordre de : - quelques dizaines de Ko pour les objets les plus récents. - quelques Mo pour les objets en génération 1. - plus de 10 Mo pour les objets en génération 2.
Lorsque le système manque de mémoire (besoin d'allocation, génération 0, 1 ou 2 pleine, etc), le GC créé pour chaque génération un graphe de dépendance de manière à déterminer quels objets peuvent être virer. Si un objet est IDisposable mais pas disposé, le GC le Dispose et le monte en génération supérieure. Lors d'un prochain GC, il sera viré. Mais si le Dispose avait été fait à la main, il aurait été viré tout de suite.
Le GC ne construit pas de graphes de dépendances (comment les maintiendrait-il à jour en permanence?) et il ne regarde pas si les objets implémentent IDisposable. En revanche, il s'intéresse aux finaliseurs (destructeurs).
Le rôle de IDisposable, ce n'est pas de libérer la mémoire des objets .NET plus tôt, c'est de libérer les ressources "non managées" référencées par des objets .NET. En lisant ce qui précède, on a l'impression qu'on peut améliorer les choses en mettant des interfaces IDisposable et des appels à Dispose partout, ce qui serait une grosse erreur. IDisposable et Dispose n'ont de sens que si les objets .NET en question référencent (directement ou indirectement) des ressources "non managées". Et l'appel à Dispose ne fait que libérer la ressource non managée et empêcher que l'objet .NET passe dans la queue de finalisation (si l'objet référence "directement" une ressource non managée, il est de bon ton qu'il ait un finaliseur, et Dispose doit inhiber le finaliseur dans ce cas). Dispose n'a aucun impact sur la façon dont le GC gère les générations d'objets.
Le résultat est que les générations 1 et 2 se remplissent et que vu la taille de ces blocs, la construction du graphe devient de plus en plus couteuse.
Non, le GC ne fonctionne pas comme ça.
Essayer de travailler ce point de prime abord. Des dumps mémoire (windgb, .Net Profiler) pourraient aussi permettre de savoir si les ressources sont bien libérées.
Cordialement,
Patrice
"Michael Moreno" a écrit dans le message de news:
Bonjour,
Il n'est pas recommandé d'utiliser directement la méthode GC.Collect()... la CLR s'occupant de nettoyer la mémoire automatiquement au moment le plus opportun pour ne pas ralentir l'exécution de l'application.
Certes mais ca ne le fait pas, le disque swappe a tout va et ca ralentit SQL Server (BDD > 200 GB). Il s'agit d'une appli financiere temps reel et les CPU sont a 50% du temps a pleine charge ou pas loin (i.e > 90%).
Si le GC ne nettoie pas la mémoire, c'est qu'il estime que le RAM ne vient pas à manquer sur le système. Combien de RAM disposez-vous sur votre serveur ?
Serveurs de test 1 et 2 GB. Chez nos clients on a en pratique 1 ou 2 GB. On a un seul cas extreme avec 512 GB. En moyenne l'appel a GC.Collect() prend 3,7 ms toutes les minutes ce qui est tres peu donc le CLR devrait le faire tout seul sauf si bien sur il ne le fait que quand l'appli est Idle ou que la charge du CPU est < 20% ce qui n'arrive jamais.
Merci.
-- Michael ---- http://michael.moreno.free.fr/ http://port.cogolin.free.fr/