这两天在写一个小程序,需要用到遍历AD域的用户,并且会定时的去刷,根据习惯简约的写法,下面的事情就发生了。
//
//
DirectoryEntry ent = new DirectoryEntry(@"LDAP://domainName/OU=xxx,DC=xxx,DC=com", "aduserName", "adPassword"); //绑定到AD指定的OU
DirectorySearcher adSear = new DirectorySearcher(ent); //创建一个search
adSear.Filter = "(&(objectCategory=person)(objectClass=user)(!(userAccountControl:1.2.840.113556.1.4.803:=2))(!(description='')))"; //这是一个过滤条件,可以忽略它
adSear.SearchScope = SearchScope.Subtree; //强制搜索方式
foreach (SearchResult resEnt in adSear.FindAll();) //遍历所有搜索到的结果
{
//do something not importent
}
adSear.Dispose(); //销毁搜索对象
ent.Dispose(); //销毁根实体
上面 这个是循环体(每次会跑20s左右,我的timer是1分钟,有设置运行判断)
表面上看没有问题,运行了10分钟以上就可以看到内存从 40左右到了50左右,继续跑会一直涨,内存泄漏已经毫无疑问了。最先我是怀疑我循环体中间的那些操作问题,把所有的三方类全不干掉,最后发现就这点代码还是会泄漏;声明了两个(ent,adSearch)也销毁了两个,怎么就内存泄漏了呢?
查看了N多资料,也考虑到非托管内存的 GC.Collect();加上也没有效果... 我甚至在msdn去看了 dispose、finalize,但是也没有结果。后来当我留意到MSDN上面关于DirectorySearcher.FindAll Method的一个remarks:【 Due to implementation restrictions, the SearchResultCollection class cannot release all of its unmanaged resources when it is garbage collected. To prevent a memory leak, you must call the Dispose method when the SearchResultCollection object is no longer needed. 】 ,我视乎明白了。
在我的代码中,没有明确的声明searchResultCollection,但是 adSear.FindAll() 他豁然存在,代码中没有任何针对它的垃圾回收,于是
\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
....................................
SearchResultCollection resRCs = adSear.FindAll();
foreach (SearchResult resEnt in resRCs)
............................
resRCs.Dispose();
{PS:这里其实也可以直接用 adSear.FindAll().Dispose();}
然后再测试,没有内存泄漏了
这么一点事情,花了我大半天的时间来折腾,把中间无数的代码注释、测试,删除....
到这里我发现,很多习惯的写法并不一定是好事,包括很多专业的程序员也可能犯错。而且这些错误要在特定的环境才能体现出来,比如我这个,如果搜索的区域小,执行的次数不是很多,你根本感觉不到内存的泄漏。
留在这里做个备忘吧。。。