C# Weak-referenced object alive after `using` block

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()))

Add Comment
1 Answer(s)

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); }  
Answered on July 16, 2020.
Add Comment

Your Answer

By posting your answer, you agree to the privacy policy and terms of service.