2

I am trying to connect and do simple functionalities such as search on an Active Directory using C#. However, I am stuck in a problem. I am using DirectorySearcher to search the directory. There are loads of entries in the directory.

This is the function

void RunThis()
{
        DirectoryEntry de = new DirectoryEntry();
        de.Path = "LDAP://" + domainName;
        de.Username = username;
        de.Password = password;
        de.AuthenticationType = AuthenticationTypes.Secure;

        DirectorySearcher deSearch = new DirectorySearcher(de);
        //Skipping properties to load
        try
        {
            deSearch.SearchScope = SearchScope.Subtree;
            SearchResultCollection rescoll = deSearch.FindAll();
            deSearch.Dispose();
            rescoll.Dispose();
        }
        catch (Exception obj)
        {
            System.Console.WriteLine("Exception in getting results. {0}",obj.Message);
        }

     }
     de.Dispose();

} // end of function 

This is a sample function I trimmed down to. I could find a lot of posts which said that calling dispose explicitly of the DirectorySearcher or ResultCollection object will solve the problem.

However, I see that the memory used by the task is increasing constantly. There isnt much else going in the other part of the code. When i comment the function, the memory usage becomes stable.

Has anyone else faced the issue and found a solution?

PS: And there is no way out. I need to do the findall :(

Andy_MSFT
  • 312
  • 3
  • 13
  • What happens if you force a gargabe collection `System.GC.Collect ();`? Does memory stabilize? If so, then that indicates either some objects aren't being disposed, or that this is normal .net memory allocation on a sytem without memory pressure. (Frustrating, I know). – agent-j Jun 15 '11 at 18:22
  • are you hooking any event in the part of code you removed ? – Yochai Timmer Jun 15 '11 at 18:27
  • @Yochai - No i didnt hook any event! The code given is very much the code. I am just calling this function from the main function! For testing right now i was calling in a for loop to see the effect. – Andy_MSFT Jun 16 '11 at 17:23
  • Hi All, Just wanted to update.. I gave up all hope and switched to C++. Did it through that and didnt use DirectoryServices namespace. Used the winldap.h etc in place. – Andy_MSFT Jun 16 '11 at 17:25
  • Can anyone tell me how to use the Disposed event? Can i use it to wait till the time the object is disposed and then only move fwd! – Andy_MSFT Jun 17 '11 at 10:13

4 Answers4

4

You aren't disposing everything if an exception is thrown: you need to use a try/finally block or the equivalent using statement, something like:

void RunThis()
{
    using (DirectoryEntry de = new DirectoryEntry())
    {
        de.Path = "LDAP://" + domainName;
        de.Username = username;
        de.Password = password;
        de.AuthenticationType = AuthenticationTypes.Secure;

        using (DirectorySearcher deSearch = new DirectorySearcher(de))
        {
            deSearch.SearchScope = SearchScope.Subtree;
            using (SearchResultCollection rescoll = deSearch.FindAll())
            {
            }
        }
    }

} // end of function 
Joe
  • 122,218
  • 32
  • 205
  • 338
  • @Andy, in that case I suggest you post the real code. And/or try a minimal simplified version of the real code to see if you get the same problem. If you do, post the simplified version. If you don't, then start adding the real code back in bit by bit until it fails. – Joe Jun 15 '11 at 18:52
1

First, you need to figure out whether it is managed or unmanaged memory that is leaking.

  1. Use perfmon to see what happens to your process '.net memory# Bytes in all Heaps' and Process\Private Bytes. Compare the numbers and the memory rises. If the rise in Private bytes outpaces the rise in heap memory, then it's unmanaged memory growth.

  2. Unmanaged memory growth would point to objects that are not being disposed (but eventually collected when their finalizer executes).

  3. If it's managed memory growth, then we'll need to see which generation/LOH (there are also performance counters for each generation of heap bytes).

  4. If it's Large Object Heap bytes, you'll want to reconsider the use and throwing away of large byte arrays. Perhaps the byte arrays can be re-used instead of discarded. Also, consider allocating large byte arrays that are powers of 2. This way, when disposed, you'll leave a large "hole" in the large object heap that can be filled by another object of the same size.

  5. A final concern is pinned memory, but I don't have any advice for you on this because I haven't ever messed with it.

agent-j
  • 27,335
  • 5
  • 52
  • 79
  • Actually the thing is, msdn accepts that using DirectoryServices.DirectorySearcher.FindAll will need to have the dispose function called explicitly or else it cant efficently release all the used resources. So i was expecting that dispose will solve the matter. But in my case it hasn't. – Andy_MSFT Jun 16 '11 at 17:32
  • Do you know where memory is leaking? Are you sure it's managed code or unmanaged? – agent-j Jun 16 '11 at 17:50
0

DirectoryEntry and DirectorySearcher both implement IDisposable. Also, you need to insure that they are disposed even in the event of an exception. I would suggest placing the construction of both inside using blocks.

EDIT: As does SearchResultCollection, so +1 to @Joe.

TrueWill
  • 25,132
  • 10
  • 101
  • 150
0

Try using a using statement instead

void RunThis()
{
        using(DirectoryEntry de = new DirectoryEntry())
        {
          de.Path = "LDAP://" + domainName;
          de.Username = username;
          de.Password = password;
          de.AuthenticationType = AuthenticationTypes.Secure;

          DirectorySearcher deSearch = new DirectorySearcher(de);
          //Skipping properties to load
          try
          {
            deSearch.SearchScope = SearchScope.Subtree;
            SearchResultCollection rescoll = deSearch.FindAll();
            deSearch.Dispose();
            rescoll.Dispose();
          }
          catch (Exception obj)
          {
            System.Console.WriteLine("Exception in getting results. {0}",obj.Message);
          }
        }
}

This will not only dispose of the DirectoryEntry but will also clean up everything else in the using block for you.

msarchet
  • 15,104
  • 2
  • 43
  • 66
  • 1
    That's a good idea, and even better if you implement it for 'deSearch` and `resColl` as well, since that is where memory can currently leak in the case of the caught exception. – Steve Townsend Jun 15 '11 at 18:23
  • @Steve does using not dispose of all `IDisposable` inside the using block? – msarchet Jun 15 '11 at 18:28
  • Nope, it's only going to `Dispose` the object to which that `using` applies. Internally-created `IDisposable`s need their own `using` as per @Joe's answer. – Steve Townsend Jun 15 '11 at 18:33