代码之家  ›  专栏  ›  技术社区  ›  mark_h

使用HttpClient异步方法c时内存泄漏#

  •  0
  • mark_h  · 技术社区  · 8 年前

    以下代码每秒调用多次。如果客户端。PostJsonAsync引发OperationCanceledException(例如,由于超时),然后引发Task。返回FromResult(默认值(T)),但如果发生这种情况,内存使用率(和CPU使用率)会迅速开始攀升,直到抛出OutOfMemoryExceptions为止(即使没有抛出异常,内存似乎总是会攀升,尽管速度要慢得多)。代码是否存在任何可能导致内存泄漏的问题(或过度使用)?

        private async Task<T> GetResponse<T>(Dictionary<string, object> args, string method)
        {
            try
            {
                var response = await _client.PostAsJsonAsync(method, args);
    
    
                return await Deserialise<T>(response.Content); 
    
            }
            catch (OperationCanceledException)
            {
                return await Task.FromResult(default(T));
            }
        }
    
        private async Task<T> Deserialise<T>(HttpContent content)
        {
            using (var stream = await content.ReadAsStreamAsync())
            using (var streamReader = new StreamReader(stream))
            using (var reader = new JsonTextReader(streamReader))
            {
                var serializer = new JsonSerializer();
                try
                {
                    return serializer.Deserialize<T>(reader);
                }
                catch (JsonSerializationException e)
                {
                    throw new ClientException("Failed to deserialize object, see inner exception and content for more details", e) { Content = reader.ReadAsString() };
                }
            }
        }
    
    1 回复  |  直到 8 年前
        1
  •  4
  •   YuvShap    8 年前

    我可以指出两件让你的内存使用率攀升的事情:

    • 正如恩科西在评论中指出的那样 PostAsJsonAsync 返回类型为 HttpResponseMessage 哪个实现 IDisposable 所以你应该处理它,只需添加一个 using 围绕它的声明。
      这就是代码泄漏的地方,也是即使没有抛出异常,内存使用率也会攀升的原因。

    • 抛出异常时,内存使用率上升得更快的原因是,除了此场景中的第一个问题之外,您还使用 Task.FromResult 这会增加内存使用的压力。
      虽然它似乎没有泄漏,但仍然会使GC工作更加困难,这就是为什么我建议您缓存返回的任务(您可以重用相同的任务实例,因为您总是返回相同的值),或者更好的是,根本不创建新任务。
      因为您的方法已标记为 async 您可以返回 default(T) 编译器将立即为您生成状态机,并将其包装到任务中(无论如何,它都会这样做,没有理由创建和等待新任务)。

    修改后的代码应如下所示:

    private async Task<T> GetResponse<T>(Dictionary<string, object> args, string method)
    {
        try
        {
            using(var response = await _client.PostAsJsonAsync(method, args))
            {
                return await Deserialise<T>(response.Content); 
            }
        }
        catch (OperationCanceledException)
        {
            return default(T);
        }
    }