這兩天在寫一個小程序,需要用到遍歷AD域的用戶,並且會定時的去刷,根據習慣簡約的寫法,下面的事情就發生了。
//////////////////////////////////////////////////////////////////////////////////
[csharp]
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
} www.2cto.com
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() 他豁然存在,代碼中沒有任何針對它的垃圾回收,於是
\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
[csharp]
....................................
SearchResultCollection resRCs = adSear.FindAll();
foreach (SearchResult resEnt in resRCs)
............................
resRCs.Dispose();
\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
{PS:這裡其實也可以直接用 adSear.FindAll().Dispose();}
然後再測試,沒有內存洩漏了
這麼一點事情,花了我大半天的時間來折騰,把中間無數的代碼注釋、測試,刪除....
到這裡我發現,很多習慣的寫法並不一定是好事,包括很多專業的程序員也可能犯錯。而且這些錯誤要在特定的環境才能體現出來,比如我這個,如果搜索的區域小,執行的次數不是很多,你根本感覺不到內存的洩漏。
留在這裡做個備忘吧。。。