views:

29

answers:

1

Hi, I'm working on a personal project that uploads and downloads big files using FTP protocol. It's working fine except the memory leak that I recently noticed. I don't know exactly what the problem is. It could be memory leak or bad programming. The memory amount used by this application increasing every second while uploading. Here is the code:

    Action action;
    int bufferSize = 16384;
    EventLogger elog = new EventLogger();
    string error = "";
    string filename = "";

    public Uploader(Action action)
    {
        this.action = action;
        filename = action.directory.Substring(action.directory.LastIndexOf('\\') + 1,
            action.directory.Length - action.directory.LastIndexOf('\\') - 1);
    }

    public bool startUpload()
    {   
        try
        {
            FtpWebRequest request = (FtpWebRequest)WebRequest.Create("ftp://*******");
            request.Method = WebRequestMethods.Ftp.ListDirectory;
            request.Credentials = new NetworkCredential("***", "***");

            FtpWebResponse response = (FtpWebResponse)request.GetResponse();

            Stream responseStream = response.GetResponseStream();
            StreamReader reader = new StreamReader(responseStream);
            List<string> files = new List<string>();
            string[] filesArr = reader.ReadToEnd().Split('\n');
            reader.Close();
            response.Close();
            foreach (string file in filesArr)
                files.Add(file.Replace("\r", ""));
            if (files.IndexOf(filename) != -1)
            {
                request = (FtpWebRequest)WebRequest.Create("ftp://***/"+filename);
                request.Method = WebRequestMethods.Ftp.DeleteFile;
                request.Credentials = new NetworkCredential("***", "***");
                response = (FtpWebResponse)request.GetResponse();
                reader.Close();
                response.Close();
                if (response.StatusCode != FtpStatusCode.FileActionOK)
                {
                    return false;
                }
            }

            request = (FtpWebRequest)WebRequest.Create("ftp://***/"+filename);
            request.Method = WebRequestMethods.Ftp.UploadFile;
            request.KeepAlive = false;
            request.UseBinary = true;

            FileStream stream = File.OpenRead(action.directory);
            byte[] buffer = new byte[bufferSize];
            Stream reqStream = request.GetRequestStream();

            SqlCommand cmd = new SqlCommand();
            cmd.CommandText = "update DIRECT_UPLOAD set COMPLETED = @com, PROGRESS = @prog, SPEED = @speed where ID = @id";
            cmd.Parameters.AddWithValue("@id", action.id);
            cmd.Parameters.AddWithValue("@com", 0);
            cmd.Parameters.AddWithValue("@prog", 0);
            cmd.Parameters.AddWithValue("@speed", 0);


            long i = 0;
            int readed = 0;
            int total = 0;
            int speed = 0;
            DateTime last = DateTime.Now;
            int lastTotal = 0;
            while ((readed = stream.Read(buffer, 0, bufferSize)) > 0)
            {
                reqStream.Write(buffer, 0, readed);
                total += readed;
                if (i % 100 == 0)
                {
                    cmd.Parameters["@com"].Value = total;
                    cmd.Parameters["@prog"].Value = (int)(((double)total / action.size) * 100);
                    int tot = 0;
                    tot = total - lastTotal;
                    int time = Convert.ToInt32((DateTime.Now - last).TotalMilliseconds);
                    speed = (int)(((double)1000.0 / time) * tot);
                    cmd.Parameters["@speed"].Value = speed;
                    if ((error = SqlProcess.sqlNonQuery(cmd)) != "")
                        throw new Exception(error);
                    last = DateTime.Now;
                    lastTotal = total;
                }

                Application.DoEvents();
                i++;
            }

            cmd.Parameters["@com"].Value = total;
            cmd.Parameters["@prog"].Value = 100;
            cmd.Parameters["@speed"].Value = 0;
            if ((error = SqlProcess.sqlNonQuery(cmd)) != "")
                throw new Exception(error);

            reqStream.Close();
            stream.Close();
        }
        catch (Exception ex)
        {
            elog.write(ex);
            return false;
        }
        return true;
    }

Thank you.

+1  A: 

Check all the objects you use here to make sure they do not need to be Dispose-d (i.e. do they implement IDisposable?). Otherwise, you will experience a leak of unmanaged resources associated with each of those objects every time this code executes.

You can use using to tidily ensure that Dispose() is called for such objects, in an exception-safe way.

Example - instead of:

SqlCommand cmd = new SqlCommand();

use this to wrap the code that uses cmd

using (SqlCommand cmd = new SqlCommand())
{
}

Note that your EventLogger class may also need to implement IDisposable if it's a custom class that wraps unmanaged resources through (for example) File or EventLog.

You can check the other built-in classes that you use here and elsewhere in your program in the MSDN docs.

Steve Townsend
Thanks for your answer. I will implement your suggestions. But is that kind of bad programming cause 200 MB extra memory use in 5 minutes? Application uses 25 MB memory at start.
Olcay
Extra memory use is not always due to leaks - use of the managed heap and Garbage Collection by the CLR means that you cannot correlate process size with actual application memory use directly. I would try this and see what happens. If you want detailed memory usage info on your process, you will have to use a CLR profiler that allows 'live' object instances to be tracked.
Steve Townsend