使用PrincipalSearcher.FindAll()时内存泄漏

Ric*_*ard 4 .net c# directoryservices userprincipal principalcontext

我也有使用插件和appdomains的长期服务,并且因使用目录服务而导致内存泄漏.请注意,我使用的是system.directoryservices.accountmanagement,但我理解它使用相同的底层ADSI API,因此容易出现相同的内存泄漏.

我查看了所有CLR内存计数器,并且内存没有泄漏,并且都是在强制GC或卸载appdomain时返回的.泄漏是私有字节,不断增长.我在这里搜索并看到了一些与使用ADSI API时内存泄漏相关的问题,但它们似乎表明只需遍历directorysearcher即可解决问题.但正如您在下面的代码中看到的那样,我在foreach块中执行此操作,但内存仍然被泄露.有什么建议?这是我的方法:

public override void JustGronkIT()
{
    using (log4net.ThreadContext.Stacks["NDC"].Push(GetMyMethodName()))
    {
        Log.Info("Inside " + GetMyMethodName() + " Method.");
        System.Configuration.AppSettingsReader reader = new System.Configuration.AppSettingsReader();
        //PrincipalContext AD = null;
        using (PrincipalContext AD = new PrincipalContext(ContextType.Domain, (string)reader.GetValue("Domain", typeof(string))))
        {
            UserPrincipal u = new UserPrincipal(AD);
            u.Enabled = true;
            //u.Surname = "ju*";
            using (PrincipalSearcher ps = new PrincipalSearcher(u))
            {
                myADUsers = new ADDataSet();
                myADUsers.ADUsers.MinimumCapacity = 60000;
                myADUsers.ADUsers.CaseSensitive = false;
                foreach (UserPrincipal result in ps.FindAll())
                {
                     myADUsers.ADUsers.AddADUsersRow(result.SamAccountName, result.GivenName, result.MiddleName, result.Surname, result.EmailAddress, result.VoiceTelephoneNumber,
                            result.UserPrincipalName, result.DistinguishedName, result.Description);
                 }
                 ps.Dispose();
            }
            Log.Info("Number of users: " + myADUsers.ADUsers.Count);
            AD.Dispose();
            u.Dispose();
        }//using AD
    }//Using log4net
}//JustGronkIT
Run Code Online (Sandbox Code Playgroud)

我对foreach循环做了以下更改,但它更好,但私有字节仍然增长,永远不会回收.

 foreach (UserPrincipal result in ps.FindAll())
 {
     using (result)
     {
         try
         {
             myADUsers.ADUsers.AddADUsersRow(result.SamAccountName, result.GivenName,           result.MiddleName, result.Surname, result.EmailAddress, result.VoiceTelephoneNumber,                                        result.UserPrincipalName, result.DistinguishedName, result.Description);
             result.Dispose();
         }
         catch
         {
             result.Dispose();
         }
     }
 }//foreach
Run Code Online (Sandbox Code Playgroud)

小智 10

我遇到了很大的内存泄漏,因为像我一样,我写了类似......

                foreach (GroupPrincipal result in searcher.FindAll())
                {
                    results.Add(result.Name);
                }
Run Code Online (Sandbox Code Playgroud)

但诀窍是FindAll本身返回一个必须处理的对象......

            using (var searchResults = searcher.FindAll())
            {
                foreach (GroupPrincipal result in searchResults)
                {
                    results.Add(result.Name);
                }
            }
Run Code Online (Sandbox Code Playgroud)


Ric*_*ard 2

我说得太早了,从长远来看,仅仅积极地调用 Dispose() 并不能解决问题。真正的解决方案?停止使用directoryservices 和directoryservices.accountmanagement 并使用System.DirectoryServices.Protocols 代替,并对我的域进行分页搜索,因为Microsoft 方面对该程序集没有泄漏。

根据要求,这里有一些代码来说明我提出的解决方案。请注意,我还使用插件架构和 appDomain,并且在完成后卸载 appdomain,但我认为鉴于 DirectoryServices.Protocols 中没有泄漏,您不必这样做。我这样做只是因为我认为使用 appDomains 可以解决我的问题,但由于它不是托管代码中的泄漏而是非托管代码中的泄漏,所以它没有任何好处。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.DirectoryServices.Protocols;
using System.Data.SqlClient;
using System.Data;
using System.Data.Linq;
using System.Data.Linq.Mapping;
using System.Text.RegularExpressions;
using log4net;
using log4net.Config;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Diagnostics;
using System.IO;

namespace ADImportPlugIn {

    public class ADImport : PlugIn
    {

        private ADDataSet myADUsers = null;
        LdapConnection _LDAP = null;
        MDBDataContext mdb = null;
        private Orgs myOrgs = null;

        public override void JustGronkIT()
        {
            string filter = "(&(objectCategory=person)(objectClass=user)(!(userAccountControl:1.2.840.113556.1.4.803:=2)))";
            string tartgetOU = @"yourdomain.com";
            string[] attrs = {"sAMAccountName","givenName","sn","initials","description","userPrincipalName","distinguishedName",
            "extentionAttribute6","departmentNumber","wwwHomePage","manager","extensionName", "mail","telephoneNumber"};
            using (_LDAP = new LdapConnection(Properties.Settings.Default.Domain))
            {
                myADUsers = new ADDataSet();
                myADUsers.ADUsers.MinimumCapacity = 60000;
                myADUsers.ADUsers.CaseSensitive = false;

                try
                {
                    SearchRequest request = new SearchRequest(tartgetOU, filter, System.DirectoryServices.Protocols.SearchScope.Subtree, attrs);
                    PageResultRequestControl pageRequest = new PageResultRequestControl(5000);
                    request.Controls.Add(pageRequest);
                    SearchOptionsControl searchOptions = new SearchOptionsControl(System.DirectoryServices.Protocols.SearchOption.DomainScope);
                    request.Controls.Add(searchOptions);

                    while (true)
                    {
                        SearchResponse searchResponse = (SearchResponse)_LDAP.SendRequest(request);
                        PageResultResponseControl pageResponse = (PageResultResponseControl)searchResponse.Controls[0];
                        foreach (SearchResultEntry entry in searchResponse.Entries)
                        {
                            string _myUserid="";
                            string _myUPN="";
                            SearchResultAttributeCollection attributes = entry.Attributes;
                            foreach (DirectoryAttribute attribute in attributes.Values)
                            {
                                if (attribute.Name.Equals("sAMAccountName"))
                                {
                                    _myUserid = (string)attribute[0] ?? "";
                                    _myUserid.Trim();
                                }
                                if (attribute.Name.Equals("userPrincipalName"))
                                {
                                    _myUPN = (string)attribute[0] ?? "";
                                    _myUPN.Trim();
                                }
                                //etc with each datum you return from AD
                        }//foreach DirectoryAttribute
                        //do something with all the above info, I put it into a dataset
                        }//foreach SearchResultEntry
                        if (pageResponse.Cookie.Length == 0)//check and see if there are more pages
                            break; //There are no more pages
                        pageRequest.Cookie = pageResponse.Cookie;
                   }//while loop
              }//try
              catch{}
            }//using _LDAP
        }//JustGronkIT method
    }//ADImport class
} //namespace
Run Code Online (Sandbox Code Playgroud)