Jag håller på med en applikation för att uppdatera lite saker i en katalog. Nu vet jag inte alla detaljer när det gäller ldap, men undvik foreach. Menar du att går det mycket åt mycket minne även om du inte gör nåt i loopen? Tack för input.Äter minne!, Hjälp! DirectoryEntry.Children (LDAP)
Katalogen innehåller ca 90000 användare (OU=Users)
<code>
DirectoryEntry Users = new DirectoryEntry(ldapUserRoot, userName, password, at);
foreach (DirectoryEntry User in Users.Children)
{
// Här går det åt minne som fan.
}
</code>
Jag har provat att köra den som [MTAThread] utan resultat
Det funkar inte att köra User.Dispose(); i slutet på varje loop.
När jag kommer in i denna metod står minnet på ca 150MB men när jag kört igenom alla poster i ligger minnet på mer än 3 GB!!!
Hur får jag ner minnet, detta är ohållbart!
Tack på förhand
mvh
AndreasSv: Äter minne!, Hjälp! DirectoryEntry.Children (LDAP)
Varje gång du arbetat genom en användare kommer den gå genom listan igen.
Det kan bli mååånga poster. Idealet är att spara ner användarna i en lista, kolla hur många det är och sen använda for-loop.
<code>
DirectoryEntry Users = new DirectoryEntry(ldapUserRoot, userName, password, at);
int count = Users.Count;
for(int i = 0; i < count; i++)
{
//Gör det du ska
}
</code>
Vet inte om exakt den koden fungerar, men du förstår säkert principen. Lägg inte Users.Count inne i for-villkoret då den kommer räkna antalet poster i listan varje gång du läser villkoret (med 90 000 poster blir det 90 000 ggr du räknar fram samma sak).Sv: Äter minne!, Hjälp! DirectoryEntry.Children (LDAP)
Tycker defentivt att du skall ha en .Dispose() när du är klar med objektet i loopen. Den släpper de unmanaged resurerna som används av objektet, det borde iallafall ge en föränding i minnesåtgång.
I varje var av foreach-loopen släpps referensen till det "gamla" DirectoryEntry objektet iom detta kan Garbage Collectorn släppa tillbaka det minnet. Problemet vi har här är nog att GC inte körs och du får 90 000 objekt skräpandes i minnet. Och här skulle jag nog rekommendera att testa att trigga GC själv (Inget man egentligen skall gör men kanske ett undantag här). Dock så kommer GC att "frysa" applikationen varje gång den körs, inget som direkt märks i gränsnittet men dock kommer prestandan att gå ner, hela loppen kommer att ta längre tid.
Testa följande:
<code>
DirectoryEntry Users = new DirectoryEntry(ldapUserRoot, userName, password, at);
foreach (DirectoryEntry User in Users.Children)
{
// Gör ingenting här
User.Dispose();
}
</code>
Har du fortfarande mycket minnesbelastning prova följande:
<code>
DirectoryEntry Users = new DirectoryEntry(ldapUserRoot, userName, password, at);
foreach (DirectoryEntry User in Users.Children)
{
// Gör ingenting här
User.Close();
}
</code>
Close anropas av Dispose men Dispose säger även till för GC att den inte behöver städa det här objektet och det kan göra att objektet ligger och skräpar längre. Om du fortfarande inte får ner det prova:
<code>
DirectoryEntry Users = new DirectoryEntry(ldapUserRoot, userName, password, at);
int GCCounter = 0;
foreach (DirectoryEntry User in Users.Children)
{
// Gör ingenting här
User.Dispose();
GCCounter ++:
if (GCCounter > 1000)
{
GC.Collect();
GCCounter = 0;
}
}
</code>
Vet inte om sifran 1000 är optimal men den kan du ju testa lite olika.
När du hittat den lösning som minskar minnet sätt tillbaka koden som utför nåt i loopen och om du nu fortfarande har hög minnesåtgång får du visa oss den koden.Sv: Äter minne!, Hjälp! DirectoryEntry.Children (LDAP)
Det hela löste sig genom att först köra
GC.collect()
Men detta hjälpte inte jag var även tvungen att inkludera
GC.WaitForPendingFinalizers();
Det knepiga var att innan jag hittade den så lät jag tråden sova ett bra tag efter collect'en men det hjälpte inte.
Hur som helst, nu är jag nere i acceptabel nivå på minnet.