Каково значение AssemblyLoadContext.Unload () в. NET Core по сравнению с обычной сборкой мусора? - PullRequest
0 голосов
/ 17 февраля 2020

. NET В Core 3.0 представлен коллекционный AssemblyLoadContext, который позволяет вызывать метод Unload() для выгрузки сборок, загруженных внутри контекста.

Согласно документации (https://docs.microsoft.com/en-us/dotnet/standard/assembly/unloadability#troubleshoot -unloadability- выдает ), выгрузка асинхронная, и любая ссылка на контекст или объекты из нее будут препятствовать выгрузке контекста.

Мне было интересно, что если я потеряю ссылку на AssemblyLoadContext, это приведет к утечке (поскольку у меня больше нет контекста для вызова Unload()). Тест подтвердил, что это не приведет к утечке, и неиспользуемая сборка будет выгружена даже без явного вызова Unload():

using System;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Loader;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using NUnit.Framework;

namespace Tests.Core
{
    [TestFixture]
    public class CollectibleAssemblyLoadContextTests
    {
        private const string AssemblyName = "Test___DynamicAssembly";

        [Test]
        [TestCase(/*unload*/ true,  /*GC sessions*/ 1)]
        [TestCase(/*unload*/ false, /*GC sessions*/ 2)]
        public void ShouldExecuteAndUnload(bool unload, int expectedGcSessions)
        {
            string actual = Execute(10, unload);
            Assert.AreEqual("executed 10", actual);

            int gcSessions = 0;
            while (!IsUnloaded())
            {
                GC.Collect();
                gcSessions++;
            }

            Assert.AreEqual(expectedGcSessions, gcSessions);
        }

        [MethodImpl(MethodImplOptions.NoInlining)]
        private bool IsUnloaded()
        {
            return !AppDomain.CurrentDomain.GetAssemblies()
                .Select(x => x.GetName().Name)
                .Contains(AssemblyName);
        }

        [MethodImpl(MethodImplOptions.NoInlining)]
        private string Execute(int number, bool unload)
        {
            var source = @"
        public static class Process
        {
            public static string Execute(int i)
            {
                return $""executed {i}"";
            }
        }";
            var compilation = CSharpCompilation.Create(AssemblyName, new[] {CSharpSyntaxTree.ParseText(source)},
                new []{MetadataReference.CreateFromFile(typeof(object).Assembly.Location)},
                new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary));

            using var ms = new MemoryStream();
            compilation.Emit(ms);
            ms.Seek(0, SeekOrigin.Begin);

            var assemblyLoadContext = new AssemblyLoadContext("CollectibleContext", isCollectible: true);

            Assembly assembly = assemblyLoadContext.LoadFromStream(ms);
            if (unload)
                assemblyLoadContext.Unload();

            Type type = assembly.GetType("Process");
            MethodInfo method = type.GetMethod("Execute");
            return (string)method.Invoke(null, new object[] {number});
        }
    }
}

Этот тест также показывает, что при использовании Unload() контекст выгружается после 1 G C сеанс, без Unload() требуется 2 сеанса для выгрузки. Но может быть просто совпадением и не всегда воспроизводимым.

Итак, учитывая, что

  1. Любые ссылки на коллекционный контекст будут препятствовать выгрузке формы (поэтому можно просто вызвать Unload() после загрузки всех сборок необходимо запланировать выгрузку, когда она не будет использоваться).
  2. Даже без вызова Unload() коллекционный контекст выгружается, когда он больше не используется.

Какова цель этого Unload() метода и в чем разница между использованием Unload() и простым использованием G C?

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...