【问题标题】:C# StreamReader Close - Memory leak?C# StreamReader Close - 内存泄漏?
【发布时间】:2012-05-11 06:19:57
【问题描述】:

我编写了一个 .NET C# windows 服务,它在我们的服务器上运行了很长时间(几个月)。

昨天我检查了一下,发现它使用了 600MB 的内存。 我重新启动了服务,现在它使用了 60MB 内存。

我已经开始检查它为什么使用这么多内存。 下面的函数会导致内存泄漏吗?

我认为它缺少 StreamReader 的 .Close()。

作为测试,我已经在循环中运行了以下函数 1000 次,但我没有看到内存增加。

private static string GetTemplate(string queryparams)
{
    WebRequest request = HttpWebRequest.Create(uri);
    request.Method = WebRequestMethods.Http.Get;
    WebResponse response = request.GetResponse();
    StreamReader reader = new StreamReader(response.GetResponseStream());
    string tmp = reader.ReadToEnd();
    response.Close();
}

【问题讨论】:

    标签: c# .net visual-studio .net-4.0


    【解决方案1】:

    您的代码正在关闭响应,但不是阅读器。

    var tmp = string.Empty;
    
    using(var reader = new StreamReader(response.GetResponseStream())
    {
        tmp = reader.ReadToEnd();  
    }
    
    /// do whatever with tmp that you want here...
    

    【讨论】:

      【解决方案2】:

      所有实现IDisposable的对象,例如WebResponseStreamReader都应该被释放。

      private static string GetTemplate(string queryparams)
      {
          WebRequest request = HttpWebRequest.Create(uri);
          request.Method = WebRequestMethods.Http.Get;
          using(var response = request.GetResponse())
          using(var reader = new StreamReader(response.GetResponseStream())
             string tmp = reader.ReadToEnd();
      }
      

      【讨论】:

      • Stream 也实现了 IDisposable。
      • @spender 处理 StreamReader 将关闭(并处理)底层流。
      • 同意。但是,通常我也会包含它,以防我稍后决定转移到另一个不隐式处理的读取实现。
      【解决方案3】:

      代码不会产生内存泄漏。

      代码并不理想,正如每个人都指出的那样(会导致关闭资源比您预期的要晚),但是当 GC 开始运行并最终确定未使用的对象时,它们将被释放。

      你确定你看到了内存泄漏或者你只是假设你有一个基于一些半随机值的内存泄漏?即使没有分配对象,CLR 也可能不会释放托管堆使用的内存,如果您没有足够的内存压力(尤其是在 x64 中),GC 可能不需要运行。

      【讨论】:

      • 完全不确定内存泄漏,我只是在任务管理器中看到使用 600mb 内存的服务(我总共只有 4gb),这对我来说真的没有意义。跨度>
      • 修复你的代码:)。要确认它是否是泄漏 - 最好使用 .Net 的内存分析器(搜索 - 大量信息),但您可以通过添加对 GC.Collect 的调用来作弊,经常进行调查并查看内存使用量是否一直在增长或只是稳定在某个值。确保您知道如何重现该条件 - 否则您将如何知道行为是否发生变化。
      • Alexei,StreamReader 等有什么特别之处,如果不处理就不会导致内存泄漏?如果应用程序退出,当然,整个进程将与分配的任何资源一起被杀死。但是,在一个运行时间无限长的服务中,这可能是一个真正的问题。我错过了什么吗?
      • 如果没有释放 CLR 对象,则不会泄漏内存(错误不算在内)。所有管理本机资源的 CLR 类(如 FileStream)都具有 Dispose 和终结器,因此如果您未能释放对象,它最终将被垃圾收集并释放非托管资源。对于纯托管对象(如大多数 XXXXReader 类),没有本机部分,它们甚至没有终结器。在任何一种情况下,GC 最终都会发生,并且内存将被标记为空闲。 (考虑阅读一些关于 GC 和 IDisposable 如何协同工作的文章……)
      【解决方案4】:

      如果您想查看内存是否会增加,我建议您进行超过 1000 次迭代。如果是你的内存泄漏,每次迭代只会占用一小部分内存。

      我不确定这是否是您的内存泄漏的根源,但是当您使用完 StreamReaders 后,.Close() 您的 StreamReaders 是一种很好的做法。

      【讨论】:

        【解决方案5】:

        对于 StreamReader,最好使用“使用”,然后当对象不再在范围内时实现 IDisposable 接口。

        using (var reader = new StreamReader(FilePath))
          {
            string tmp = reader.ReadToEnd();
          }
        

        至于你的问题 1000 次不是很多递归。尝试离开应用程序几个小时并计时几十万,这将为您提供更好的指示。

        【讨论】:

          【解决方案6】:

          它可能取决于您使用它的频率,因为您不使用对 Reader 的 Dispose() 的 esplicit 调用。为了确保您在这些行中尽了最大努力,请将它们写下来,如下所示:

          private static string GetTemplate(string queryparams)
          {
              WebRequest request = HttpWebRequest.Create(uri);
              request.Method = WebRequestMethods.Http.Get;
              WebResponse response = request.GetResponse();
          
              using(StreamReader reader = new StreamReader(response.GetResponseStream())){
          
                   string tmp = reader.ReadToEnd();
                   response.Close();
              }
          
              // here will be called Dispose() of the reader 
              // automatically whenever there is an exception or not.
          }
          

          【讨论】:

          • ResponseStream 和 Response 怎么样?两者都实现了 IDisposable 并且应该被处置。因此,您的代码缺少 2 个 using 语句。
          • 我明白我错过了读者的关闭:)。最大的问题是,这会导致 600mb 的内存使用吗?
          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2016-01-27
          • 2010-11-11
          • 2017-02-18
          • 1970-01-01
          • 2015-11-19
          • 2014-11-01
          相关资源
          最近更新 更多