C# WCF Memory Leak

KeepXtreme

Lt. Commander
Registriert
Sep. 2008
Beiträge
1.390
Hi zusammen,

ich hab ein etwas... merkwürdiges Problem mit meinem WCF-Service (mittels IIS gehosted mit eigenem Pool): Bei jedem Aufruf der GetAll()-Methode steigt der RAM-Bedarf des Pools um ~12MB an und wird nicht mehr freigegeben:

Code:
[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single)]
    public class relay : Irelay
    {
        private static List<Bereich> allFaculties;
        private static DateTime lastUpdated;

        public List<Bereich> GetAll() {
            if (allFaculties == null || DateTime.Compare(lastUpdated, DateTime.Now.AddDays(-1)) < 1)
            {
                allFaculties = GetDataFromDb();
                lastUpdated = DateTime.Now;
            }

            return allFaculties;
        }

        private List<Bereich> GetDataFromDb()
        {
            allFaculties = new List<Bereich>();
            using (DatabaseConnection con1 = new DatabaseConnection())
            {
                using (MySqlDataReader bReader = con1.Query("SELECT BereichsKey, Name FROM Bereiche ORDER BY ID ASC"))
                {
                    if (bReader.HasRows)
                    {
                        while (bReader.Read())
                        {
                            Bereich b = new Bereich(bReader["BereichsKey"].ToString(), bReader["Name"].ToString());
                            allFaculties.Add(b);
                        }
                    }
                }
                using (DatabaseConnection con2 = new DatabaseConnection())
                {
                    foreach (Bereich b in allFaculties)
                    {
                        using (MySqlDataReader iReader = con1.Query(String.Format("SELECT Nummer, Logoname, Name FROM Institute WHERE `Bereich`='{0}' ORDER BY Name ASC", b.key)))
                        {
                            if (iReader.HasRows)
                            {

                                while (iReader.Read())
                                {
                                    Institut i = new Institut(iReader["Nummer"].ToString(), iReader["Name"].ToString());
                                    b.addInstitute(i);
                                    try
                                    {
                                        i.logoName = iReader["Logoname"].ToString();
                                    }
                                    catch
                                    {
                                        i.logoName = String.Empty;
                                    }
                                    using (MySqlDataReader vReader = con2.Query(String.Format("SELECT * FROM MPPK WHERE `Bereich`='{1}' AND `Institut`='{0}' ORDER BY Titel ASC", i.key, i.parent.key)))
                                    {
                                        if (vReader.HasRows)
                                        {
                                            while (vReader.Read())
                                            {
                                                DateTime lastUpdated;
                                                try
                                                {
                                                    lastUpdated = (DateTime)vReader["Stand"];
                                                }
                                                catch
                                                {
                                                    lastUpdated = DateTime.MinValue;
                                                }
                                                Vorlesung v = new Vorlesung(vReader["Nummer"].ToString(), vReader["Titel"].ToString(), lastUpdated);
                                                i.addLecture(v);
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
            return allFaculties;
        }
//(...weitere Methoden...)
    }
}

Die Klasse DatabaseConnection:
Code:
using MPP_Server.Properties;
using MySql.Data.MySqlClient;
using System;

public sealed class DatabaseConnection : IDisposable
{
    MySqlConnection connection;

    public DatabaseConnection()
    {
        MySqlConnectionStringBuilder myCSB = new MySqlConnectionStringBuilder();
        myCSB.Server = Settings.Default.mysqlHost;
        myCSB.Port = Settings.Default.mysqlPort;
        myCSB.UseCompression = true;
        myCSB.UserID = Settings.Default.mysqlUser;
        myCSB.Password = Settings.Default.mysqlPass;
        myCSB.Database = Settings.Default.mysqlDatabase;
        connection = new MySqlConnection(myCSB.ConnectionString);
        connection.Open();
    }

    public MySqlDataReader Query(string command) {
        return Query(new MySqlCommand(command));
    }

    public MySqlDataReader Query(MySqlCommand command)
    {
        command.Connection = connection;
        return command.ExecuteReader();
    }

    public void Dispose()
    {
        try
        {
            connection.Dispose();
        }
        catch
        { }
        finally
        {
            connection = null;
        }
    }

    public void prepareCommand(MySqlCommand cmd) {
        cmd.Connection = connection;
        cmd.Prepare();
    }
}
Dispose() war hier schon der Versuche, einen möglichen Leak durch die MySQLConnection zu finden...


Der Aufruf des Clients erfolgt wie folgend:
Code:
IrelayClient mppService = new IrelayClient();
Bereich.allFaculties = await mppService.GetAllAsync();
try
{
       mppService.CloseAsync().RunSynchronously();
}
 catch
{
       mppService.Abort();
}

kann mir jemand erklären, wo hier ein Memoryleak sein könnte..?
 
Zuletzt bearbeitet:
Ich sehe auf den ersten Blick nichts kritisches.

Hast du das mal über längere Zeit beobachtet? Der GC ist nicht-deterministisch. Nur weil Speicher freigegeben werden könnte, heißt das noch nicht, dass er zeitnah freigegeben wird.
 
ich werds mal beobachten. Ich hab die Klasse oben noch angepasst, in der Hoffnung, nicht jedes Mal ein neues Objekt erstellen zu müssen... Ich seh den Unterschied auch insofern, dass bei jedem Request nur noch 500kB zusätzlich belegt werden...

es sind wohl wirklich die lokalen Variablen, die das Problem verursachen... Oo
 
Such dir nen Memory profiler. dotMemory von JetBrains ist ganz gut. Setze den dann auf deine Anwendung an.
Da könntest du dann eventuell herausfinden, wozu der Speicher allokiert wird. Du kannst dann, je nach Profiler, auch den GC anstossen (dotMemory kann das zum Beispiel) und schauen, ob der Speicher korrekt freigegeben wird.
Das einzige Problem könnte der IIS-Pool sein. Ich weiss nicht, inwiefern der profiler damit funktioniert oder ob da kein Unterschied ist.
 
danke, den Memory profile kannte ich noch nicht. Ich werd das Verhalten des Servers mal weiter beobachten und mir dann überlegen, ob sich der Aufwand lohnt. Trotzdem danke für den Tipp =)
 
Zurück
Oben