0

Based on this previous question, I wanted to see if not disposing an HttpClient object creates leaks (which does, but nothing new here). However, what I've also found is that if I don't dispose a HttpClient object, the returned HttpResponseMessage is not garbage collected.

Here's the test. I've provided a reproducing fiddle here.

using System;
using System.Net.Http;

namespace HttpClientTest
{
    internal static class Program
    {
        private static T CallInItsOwnScope<T>(Func<T> getter)
        {
            return getter();
        }

        private static void Main()
        {
            var client = new HttpClient();
            var wRef2 = CallInItsOwnScope(() =>
            {
                using (var response = client.GetAsync(new Uri("https://postman-echo.com/get?foo1=bar1&foo2=bar2")).Result)
                {
                    return new WeakReference(response);
                }
            });
            GC.Collect();
            GC.WaitForPendingFinalizers();
            Console.WriteLine($"Alive: {wRef2.IsAlive}");

            client.Dispose();

            GC.Collect();
            GC.WaitForPendingFinalizers();
            Console.WriteLine($"Alive: {wRef2.IsAlive}");

            Console.ReadKey();
        }
    }
}

Compiling under .NET Framework 4.7.2, The output is as follows:

Alive: True
Alive: False

Edit: using .NET Core, I get the expected result:

Alive: False
Alive: False

My question is, why HttpResponseMessage is still alive after I disposed it, but only if I don't dispose it's creator?

(Looking at HttpClient's sources I found that there is indeed a reference to the HttpResponseMessage being held, but this is deep into async land for me to really understand what's going on (SetTaskCompleted -> TaskCompletionSource<>.TrySetResult()))

Leonardo Herrera
  • 8,388
  • 5
  • 36
  • 66
  • Why do you want to `Dispose` your `HttpClient`. The general recommendation is to let them live forever. – Flydog57 Jul 14 '20 at 01:27
  • I'm trying to understand why an `HttpResponse` object would leak. I'm not worried about `HttpClient`, in fact, I fear disposing it too often could lead to socket exhaustion. – Leonardo Herrera Jul 14 '20 at 18:17
  • I'm getting `False`, `False`, so there's some details missing here if you want others to see what you're seeing. – Damien_The_Unbeliever Jul 15 '20 at 15:53
  • I have a plain .net framework 4.7.2 console application, and it returns the results above. Using a .net core console application returns false for both cases. – Leonardo Herrera Jul 15 '20 at 15:56
  • You should wait at least a bit before jumping on closing a question, Damien. Here's a Fiddle for you to see. https://dotnetfiddle.net/Xo6iKu – Leonardo Herrera Jul 15 '20 at 16:07

1 Answers1

0

First of all, we need look at this MSDN - HttpClient Class.

HttpClient is intended to be instantiated once per application, rather than per-use. See Remarks.

Anyway, here is what you missed.

public void CallServiceTest()
{
    var wRef2 = CallInItsOwnScope(() =>
    {
        // HttpClient -> HttpMessageInvoker(IDisposable), so it can be disposed.
        using (var client = new HttpClient())
        { 
            using (var response = client.GetAsync(new Uri("https://postman-echo.com/get?foo1=bar1&foo2=bar2")).Result)
            {
                return new WeakReference(response);
            }
        }
    });
    GC.Collect();
    GC.WaitForPendingFinalizers();
    Assert.IsFalse(wRef2.IsAlive);
}

jornathan
  • 646
  • 5
  • 13