C# Объект со слабыми ссылками жив после блока `using` - PullRequest
0 голосов
/ 14 июля 2020

Основываясь на на этом предыдущем вопросе , я хотел посмотреть, не приводит ли не удаление объекта HttpClient к утечкам (что происходит, но здесь нет ничего нового). Однако я также обнаружил, что если я не удаляю объект HttpClient, возвращаемый HttpResponseMessage не является сборщиком мусора.

Вот тест. Здесь я предоставил воспроизводящую скрипку .

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

Компиляция под. NET Framework 4.7.2, результат будет следующим:

Alive: True
Alive: False

Edit: using. NET Core, я получаю ожидаемый результат:

Alive: False
Alive: False

Мой вопрос: почему HttpResponseMessage все еще жив после того, как я его удалил, но только если я не избавляться от его создателя?

(Глядя на источники HttpClient, я обнаружил, что действительно есть ссылка на HttpResponseMessage, удерживаемый, но для меня это глубоко в асинхронной c земле чтобы действительно понять, что происходит (SetTaskCompleted -> TaskCompletionSource<>.TrySetResult()))

1 Ответ

0 голосов
/ 14 июля 2020

Прежде всего, нам нужно взглянуть на этот MSDN - HttpClient Class .

HttpClient предназначен для создания экземпляра один раз для каждого приложения, а не для каждого использования. См. Примечания.

В любом случае, вот что вы пропустили.

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

...