Fuite de mémoire lors de l'utilisation de DirectorySearcher.FindAll()
J'ai un long processus en cours d'exécution qui doit faire beaucoup de requêtes sur Active Directory assez souvent. Pour cet effet, j'ai été en utilisant le Système.DirectoryServices espace de noms, à l'aide de la DirectorySearcher et DirectoryEntry classes. J'ai remarqué une fuite de mémoire dans l'application.
Il peut être reproduite avec ce code:
while (true)
{
using (var de = new DirectoryEntry("LDAP://hostname", "user", "pass"))
{
using (var mySearcher = new DirectorySearcher(de))
{
mySearcher.Filter = "(objectClass=domain)";
using (SearchResultCollection src = mySearcher.FindAll())
{
}
}
}
}
La documentation de ces classes-dire qu'ils auront une fuite de mémoire si dispose() n'est pas appelée. J'ai essayé sans disposer du bien, c'est juste des fuites plus de la mémoire dans ce cas. J'ai testé avec les deux versions framework 2.0 et 4.0 quelqu'un A rencontré ce avant? Existe-il des solutions?
Mise à jour: j'ai essayé d'exécuter le code dans un autre domaine d'application, et il ne semble pas aider non plus.
- while(true)... ?
- C'est juste pour illustrer le problème, dans le réel de l'application, il n'est pas comme ça, évidemment.
- Comment pouvez-vous remarqué une fuite de mémoire dans l'application"?
- Malheureusement, en utilisant while(true)... à cet endroit permettrait de prévenir la DirectorySearcher et la DirectoryEntry être éliminés correctement. Vous pourriez le mettre au premier niveau de "l'aide" à la place, et de vérifier ce qui se passe.
- en fait, j'ai essayé que le premier, avec les mêmes résultats. Il donne le même résultat dans les deux cas. Édité mon code en question et si déplacé à l'extérieur de la boucle.
- Gencer - avez-vous essayé d'inspecter l'utilisation de la mémoire avec WinDebug et les extensions gérées? J'ai réussi à troubleshooted des fuites de mémoire de l'utiliser.
- J'ai utilisé WinDbg et aussi Ants profiler, et le problème semble être dans la mémoire non managée partie, qui ne cesse de croître.
- Gencer - vous dire "dans le réel de l'application, il n'est pas comme ça, évidemment". Ce qui pourrait ne pas être si évident, c'est que nous ne pouvons pas vraiment d'indices, à moins que nous savons ce que votre code réel, c'est comme.
- dans mon précédent code de la boucle while a été autour de la findall(), qui n'est pas un scénario réaliste. Le point du code ci-dessus est pour illustrer la fuite de mémoire, ce qui semble se produire chaque fois que FindAll() est appelée. Il n'est pas destiné à un morceau de l'application réelle.
- Gencer de nouveau... sans voir le code réel, tout ce que nous faisons c'est de faire des suppositions sauvages dans l'obscurité.
- Qui est le véritable code de la fuite de la mémoire. Vous pouvez l'exécuter, et il sera de fuite.. Si je peux éviter le code ci-dessus à partir de la fuite, alors je peux l'utiliser dans mon code, qui a beaucoup de code supplémentaire de ne pas pertinent pour cette question, c'est pourquoi j'ai sauté dessus.
- lequel de ces objets est une fuite? Avez-vous trouver de l'aide SOS.dll f.e.?
- Ants profiler montre "une mémoire non managée" que la partie qui est en augmentation. Il n'est pas directement quelque chose à l'intérieur de la GC de l'atteindre..
- Je me souviens que j'ai frappé ce problème dans le .net 2 jours, vous pourriez être mieux d'utiliser le LDAP classes de parler à ActiveDirectory, plutôt que de la vieille ADSI gâchis!!!
- Ringrose, qui LDAP classes parlez-vous?
Vous devez vous connecter pour publier un commentaire.
Aussi étrange que cela puisse être, il semble que la fuite de mémoire se produit uniquement si vous ne le faites pas n'importe quoi avec les résultats de la recherche. Modifier le code dans la question suivante n'a pas de fuite de mémoire:
Ce qui semble être causée par interne searchObject champ ayant l'initialisation tardive , en regardant SearchResultCollection avec Réflecteur :
L'éliminer ne fermera pas non managé poignée à moins que searchObject est initialisé.
L'appel de la méthode MoveNext sur le ResultsEnumerator appelle la SearchObject sur la collection ainsi faire en sorte qu'il est disposé correctement.
La fuite dans mon application est en raison de certaines autres non géré tampon pas libérée correctement et le test que j'ai fait était trompeuse. Le problème est résolu maintenant.
Le wrapper géré n'a pas vraiment de fuite de quoi que ce soit. Si vous n'appelez pas
Dispose
des ressources non utilisées pourront encore être récupérés lors de la collecte des ordures.Cependant, le code managé est un wrapper sur le dessus de la COM ADSI API et lorsque vous créez un
DirectoryEntry
le code sous-jacent qui fera appel à laADsOpenObject
function. Le retour de l'objet COM est libéré lorsque leDirectoryEntry
est disposé ou au cours de finalisation.Il est documenté fuite de mémoire lorsque vous utilisez le ADsOpenObject API avec un ensemble d'informations d'identification et un WinNT fournisseur:
Cependant, la fuite n'est que de 8 octets et et aussi loin que je peux voir vous utilisez le fournisseur LDAP et non pas le dossier WinNT fournisseur.
Appel
DirectorySearcher.FindAll
permettra d'effectuer une recherche qui requiert beaucoup de nettoyage. Ce nettoyage est effectué dansDirectorySearcher.Dispose
. Dans votre code, ce nettoyage est effectué à chaque itération de la boucle et non pas lors de la collecte des ordures.À moins qu'il y est vraiment un sans-papiers fuite de mémoire dans le LDAP ADSI API la seule explication que je peux trouver, c'est la fragmentation du segment non géré. L'ADSI API est mis en œuvre par un processus de serveur COM et chaque recherche ne sera probablement allouer de la mémoire sur le segment non géré de votre processus. Si cette mémoire est fragmentée le tas peut se développer lorsque l'espace est alloué pour de nouvelles recherches.
Si mon hypothèse est vraie, une option serait d'exécuter les recherches dans un autre domaine d'application qui peuvent ensuite être récupérée pour décharger ADSI et de recycler la mémoire. Cependant, même si la fragmentation de la mémoire peut augmenter la demande de mémoire non managée je m'attends à ce qu'il y aurait une limite supérieure à la quantité de mémoire non managée est nécessaire. Sauf si bien sûr vous avez une fuite.
Aussi, vous pouvez essayer de jouer avec le
DirectorySearcher.CacheResults
de la propriété. En fixant àfalse
éliminer la fuite?delete
/free
la mémoire quand il décharge (sauf s'il a une fuite), mais le segment peut encore être mappés dans le processus.En raison de la mise en œuvre de restrictions, le SearchResultCollection classe ne peut pas libérer l'ensemble de ses ressources non managées, quand il est de ces ordures. Pour éviter une fuite de mémoire, vous devez appeler la méthode dispose lors de la SearchResultCollection objet n'est plus nécessaire.
http://msdn.microsoft.com/en-us/library/system.directoryservices.directorysearcher.findall.aspx
EDIT:
J'ai été en mesure de reproduire le de fuite apparente à l'aide de l'analyseur de performances, et en ajoutant un compteur Octets Privés sur le nom de processus de l'application de test (Expériences.vshost pour moi )
le compteur Octets Privés permettra d'augmenter de façon constante, tandis que l'application est en boucle, ça commence autour de 40 000 000, puis croît d'environ un million d'octets toutes les quelques secondes. La bonne nouvelle, c'est que le compteur revient à la normale (35,237,888) lorsque vous mettez fin à l'application, donc une sorte de nettoyage est enfin passé.
J'ai joint une capture d'écran de ce que perfmon ressemble quand sa fuite
Mise à jour:
J'ai essayé quelques solutions de contournement, comme désactiver la mise en cache sur le DirectoryServer objet, et il n'a pas aidé.
La FindOne() la commande n'a pas de fuite de mémoire, mais je ne suis pas sûr de ce que vous avez à faire pour rendre cette option de travail pour vous, sans doute modifier le filtre en permanence, sur mon contrôleur AD, il y a juste un seul domaine, de sorte findall & findone donnent le même résultat.
J'ai aussi essayé de files d'attente de 10 000 threadpool les travailleurs à faire la même DirectorySearcher.FindAll(). Il a fini beaucoup plus vite, mais il a encore une fuite de mémoire, et en fait octets privés sont allés jusqu'à environ 80 mo, au lieu de 48 mo pour le "normal" de fuite.
Donc, pour cette question, si vous pouvez faire FindOne() le travail pour vous, vous avez une solution de contournement. Bonne Chance!
Avez-vous essayé
using
etDispose()
?Info de ici
Mise à jour
Essayez d'appeler
de.Close();
avant la fin de l'aide.Je n'ai pas vraiment avoir un Domaine Actif de Service pour le tester sur, désolé.
Trouvé une façon rapide et sale autour de ce.
J'ai eu un problème similaire dans mon programme avec la croissance de la mémoire, mais en changeant .GetDirectoryEntry().Propriétés("cn").Valeur à
.GetDirectoryEntry().Propriétés("cn").De la valeur.ToString avec un si avant la main afin de s'en assurer .la valeur n'était pas nulle
j'ai été en mesure de dire GC.Recueillir de se débarrasser de la valeur temporaire dans mon foreach. Il ressemble à l' .la valeur a fait l'objet vivant, plutôt que de les lui permettant d'être recueillies.