【问题标题】:FTP upload - memory leak problemFTP上传-内存泄漏问题
【发布时间】:2011-04-25 10:45:13
【问题描述】:

我正在开发一个使用 FTP 协议上传和下载大文件的个人项目。除了我最近注意到的内存泄漏之外,它工作正常。我不知道到底是什么问题。这可能是内存泄漏或糟糕的编程。此应用程序使用的内存量在上传时每秒都在增加。代码如下:

    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;
    }

谢谢。

【问题讨论】:

  • 首先,您应该对所有实现 IDisposable 的类实例(如 Stream)使用 using 语句,以确保即使发生异常也能关闭和清理它们

标签: c# memory-leaks upload ftp


【解决方案1】:

检查您在此处使用的所有对象以确保它们不需要是Dispose-d(即它们是否实现了IDisposable?)。否则,每次执行此代码时,您都会遇到与每个对象关联的非托管资源泄漏。

您可以使用using 整齐地确保以异常安全的方式为此类对象调用Dispose()

示例 - 代替:

SqlCommand cmd = new SqlCommand();

使用它来包装使用cmd的代码

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

请注意,如果您的 EventLogger 类是通过(例如)FileEventLog 包装非托管资源的自定义类,则它可能还需要实现 IDisposable

您可以在 MSDN 文档中查看您在此处和其他地方使用的其他内置类。

【讨论】:

  • 感谢您的回答。我会落实你的建议。但是这种糟糕的编程是否会导致 5 分钟内使用 200 MB 的额外内存?应用程序在启动时使用 25 MB 内存。
  • 额外的内存使用并不总是由于泄漏 - CLR 使用托管堆和垃圾收集意味着您无法直接将进程大小与实际应用程序内存使用相关联。我会试试这个,看看会发生什么。如果您想要有关进程的详细内存使用信息,则必须使用允许跟踪“活动”对象实例的 CLR 分析器。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2016-07-28
相关资源
最近更新 更多