<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12

      聊一聊 .NET 的 AssemblyLoadContext 可插拔程序集

      一:背景

      1. 講故事

      最近在分析一個崩潰dump時,發現禍首和AssemblyLoadContext有關,說實話這東西我也比較陌生,后來查了下大模型,它主要奔著替代 .NetFrameWork 時代的 AppDomain 的,都是用來做晚期加卸載,實現對宿主程序的可插拔,AppDomain.Create 是在AppDomain級別上,后者是在 Assembly 級別上。

      二:Assembly 插拔分析

      1. 一個簡單的案例

      簡單來說這東西可以實現 Assembly 的可插拔,這個小案例有三個基本元素。

      1. IPlugin 組件接口

      這塊比較簡單,新建一個類庫,里面主要就是組件需要實現的接口。

      
      namespace MyClassLibrary.Interfaces
      {
          public interface IPlugin
          {
              string Name { get; }
              string Version { get; }
              void Execute();
              string GetResult();
          }
      }
      
      
      1. SamplePlugin 組件實現

      新建一個組件,完成這些接口方法的實現。

      
          public class SamplePlugin : IPlugin
          {
              public string Name => "Sample Plugin";
              public string Version => "1.0.0";
      
              public void Execute()
              {
                  Console.WriteLine("SamplePlugin is executing...");
              }
      
              public string GetResult()
              {
                  return "Hello from SamplePlugin!";
              }
          }
      
      
      1. 自定義的 CustomAssemblyLoadContext 上下文

      最后就是在調用處自定義下 AssemblyLoadContext 以及簡單調用,參考代碼如下:

      
      namespace Example_1_6
      {
          internal class Program
          {
              static void Main(string[] args)
              {
                  Console.WriteLine("=== 插件系統啟動 ===");
      
                  // 設置插件目錄
                  string pluginsPath = @"D:\sources\woodpecker\Test\MyClassLibrary\bin\Debug\net8.0\";
      
                  Console.WriteLine($"插件路徑: {pluginsPath}");
      
                  var dllFile = Directory.GetFiles(pluginsPath, "MyClassLibrary.dll").FirstOrDefault();
      
                  var _loadContext = new CustomAssemblyLoadContext("MyPluginContext", pluginsPath);
      
                  var assembly = _loadContext.LoadAssembly(dllFile);
      
                  var type = assembly.GetType("MyClassLibrary.SamplePlugin");
      
                  IPlugin plugin = (IPlugin)Activator.CreateInstance(type);
      
                  Console.WriteLine($"- {plugin.Name} v{plugin.Version}");
      
                  Console.WriteLine($"\n執行插件: {plugin.Name} v{plugin.Version}");
                  plugin.Execute();
                  string result = plugin.GetResult();
                  Console.WriteLine($"插件返回: {result}");
      
                  Console.ReadKey();
              }
          }
      
          public class CustomAssemblyLoadContext : AssemblyLoadContext
          {
              private readonly string _dependenciesPath;
      
              public CustomAssemblyLoadContext(string name, string dependenciesPath)
                  : base(name, isCollectible: true)
              {
                  _dependenciesPath = dependenciesPath;
              }
      
              public Assembly LoadAssembly(string assemblyPath)
              {
                  return LoadFromAssemblyPath(assemblyPath);
              }
      
              public new void Unload()
              {
                  base.Unload();
              }
          }
      }
      
      

      將代碼運行起來,可以看到插件代碼得到執行。

      2. 組件已經插上了嗎

      plugin中的方法都已經執行了,那 MyClassLibrary.dll 自然就插上去了,接下來如何驗證呢?可以使用 windbg 的 !dumpdomain 命令即可。

      
      0:015> !dumpdomain
      --------------------------------------
      System Domain:      00007ff8e9d4b150
      LowFrequencyHeap:   00007FF8E9D4B628
      HighFrequencyHeap:  00007FF8E9D4B6B8
      StubHeap:           00007FF8E9D4B748
      Stage:              OPEN
      Name:               None
      --------------------------------------
      Domain 1:           00000211d617dc80
      LowFrequencyHeap:   00007FF8E9D4B628
      HighFrequencyHeap:  00007FF8E9D4B6B8
      StubHeap:           00007FF8E9D4B748
      Stage:              OPEN
      Name:               clrhost
      Assembly:           00000211d613e560 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App\8.0.16\System.Private.CoreLib.dll]
      ClassLoader:        00000211D613E5F0
        Module
        00007ff889d54000    C:\Program Files\dotnet\shared\Microsoft.NETCore.App\8.0.16\System.Private.CoreLib.dll
      
      ...
      
      Assembly:           000002118052b0d0 [D:\sources\woodpecker\Test\MyClassLibrary\bin\Debug\net8.0\MyClassLibrary.dll]
      ClassLoader:        000002118052B160
        Module
        00007ff88a11c060    D:\sources\woodpecker\Test\MyClassLibrary\bin\Debug\net8.0\MyClassLibrary.dll
      
      

      從卦中可以清晰的看到 MyClassLibrary.dll 已經成功的送入。

      3. 組件如何卸載掉

      能不能卸載掉,其實取決于你在 new AssemblyLoadContext() 時塞入的 isCollectible 字段決定的,如果為true就是一個可卸載的程序集,參考代碼如下:

      
              public CustomAssemblyLoadContext(string name, string dependenciesPath)
                  : base(name, isCollectible: true)
              {
                  _dependenciesPath = dependenciesPath;
              }
      
      

      其次要知道的是卸載程序集是一個異步操作,不要以為調用了 UnLoad() 就會立即卸載,它只是起到了一個標記刪除的作用,只有程序集中的實例無引用根了,即垃圾對象的時候,再后續由 GC 來實現卸載。

      這一塊我們可以寫段代碼來驗證下,我故意將邏輯包裝到 DoWork() 方法中,然后處理完之后再次觸發GC,修改后的代碼如下:

      
          internal class Program
          {
              static void Main(string[] args)
              {
                  DoWork();
      
                  GC.Collect();
                  GC.WaitForPendingFinalizers();
      
                  Console.WriteLine("GC已觸發,請再次觀察 Assembly 是否被卸載...");
      
                  Console.ReadLine();
              }
      
              static void DoWork()
              {
                  Console.WriteLine("=== 插件系統啟動 ===");
      
                  // 設置插件目錄
                  string pluginsPath = @"D:\sources\woodpecker\Test\MyClassLibrary\bin\Debug\net8.0\";
      
                  Console.WriteLine($"插件路徑: {pluginsPath}");
      
                  var dllFile = Directory.GetFiles(pluginsPath, "MyClassLibrary.dll").FirstOrDefault();
      
                  var _loadContext = new CustomAssemblyLoadContext("MyPluginContext", pluginsPath);
      
                  var assembly = _loadContext.LoadAssembly(dllFile);
      
                  var type = assembly.GetType("MyClassLibrary.SamplePlugin");
      
                  IPlugin plugin = (IPlugin)Activator.CreateInstance(type);
      
                  Console.WriteLine($"- {plugin.Name} v{plugin.Version}");
      
                  Console.WriteLine($"\n執行插件: {plugin.Name} v{plugin.Version}");
                  plugin.Execute();
                  string result = plugin.GetResult();
                  Console.WriteLine($"插件返回: {result}");
      
                  _loadContext.Unload();
      
                  Console.WriteLine("程序集已標記為卸載... 請觀察 Assembly 是否被卸載...");
                  Console.ReadKey();
              }
          }
      
      

      從卦中可以看到確實已經不再有 MyClassLibrary.dll 程序集了,但托管堆上還有 CustomAssemblyLoadContext 死對象,當后續GC觸發時再回收,用windbg驗證如下:

      
      0:014> !dumpobj /d 238e9c464c8
      Name:        Example_1_6.CustomAssemblyLoadContext
      MethodTable: 00007ff88a06f098
      EEClass:     00007ff88a079008
      Tracked Type: false
      Size:        88(0x58) bytes
      File:        D:\sources\woodpecker\Test\Example_1_6\bin\Debug\net8.0\Example_1_6.dll
      Fields:
                    MT    Field   Offset                 Type VT     Attr            Value Name
      00007ff889e870a0  4001116       30        System.IntPtr  1 instance 000002388042A8F0 _nativeAssemblyLoadContext
      00007ff889dd5fa8  4001117        8        System.Object  0 instance 00000238e9c46520 _unloadLock
      0000000000000000  4001118       10                       0 instance 0000000000000000 _resolvingUnmanagedDll
      0000000000000000  4001119       18                       0 instance 0000000000000000 _resolving
      0000000000000000  400111a       20                       0 instance 0000000000000000 _unloading
      00007ff889e8ec08  400111b       28        System.String  0 instance 0000023880006a30 _name
      00007ff889e3a5f0  400111c       38         System.Int64  1 instance                0 _id
      00007ff889f2f108  400111d       40         System.Int32  1 instance                1 _state
      00007ff889ddd070  400111e       44       System.Boolean  1 instance                1 _isCollectible
      00007ff88a0ed120  4001114      a00 ...Private.CoreLib]]  0   static 00000238e9c46550 s_allContexts
      00007ff889e3a5f0  4001115      bc0         System.Int64  1   static                1 s_nextId
      0000000000000000  400111f      a08 ...yLoadEventHandler  0   static 0000000000000000 AssemblyLoad
      0000000000000000  4001120      a10 ...solveEventHandler  0   static 0000000000000000 TypeResolve
      0000000000000000  4001121      a18 ...solveEventHandler  0   static 0000000000000000 ResourceResolve
      0000000000000000  4001122      a20 ...solveEventHandler  0   static 0000000000000000 AssemblyResolve
      0000000000000000  4001123      a28                       0   static 0000000000000000 s_asyncLocalCurrent
      00007ff889e8ec08  4000001       48        System.String  0 instance 0000023880006938 _dependenciesPath
      
      0:014> !gcroot 238e9c464c8
      Caching GC roots, this may take a while.
      Subsequent runs of this command will be faster.
      
      Found 0 unique roots.
      
      

      三:總結

      有時候感嘆 知識無涯人有涯,在 dump分析中不斷的螺旋式提升,理論指導實踐,實踐反哺理論。
      圖片名稱

      posted @ 2025-08-30 12:19  一線碼農  閱讀(989)  評論(1)    收藏  舉報
      主站蜘蛛池模板: 沙河市| 男女性高爱潮免费网站| 欧洲熟妇熟女久久精品综合 | 色翁荡息又大又硬又粗又视频图片 | 平乐县| 成人区人妻精品一区二区| 国产高清在线不卡一区| 又大又紧又粉嫩18p少妇| 久久这里只精品国产2| 午夜精品一区二区三区成人| 亚洲夂夂婷婷色拍ww47| 人人妻人人狠人人爽天天综合网 | 精品国偷自产在线视频99| 亚洲精品中文字幕尤物综合| 亚洲人成电影网站 久久影视| 精品人妻伦九区久久69| 东京热人妻无码一区二区av| 中文字幕日韩一区二区三区不卡| 又爽又黄无遮挡高潮视频网站| 亚洲AV日韩精品久久久久| 丰满少妇人妻久久久久久| 国产一区二区精品偷系列| 亚洲一区二区精品动漫| 熟女系列丰满熟妇AV| 元码人妻精品一区二区三区9| 人人妻人人玩人人澡人人爽| 亚洲精品国模一区二区| 国产精品一二二区视在线| 最新国产精品拍自在线观看| 左贡县| 亚洲国产色一区二区三区| 亚洲另类激情专区小说婷婷久| 又大又粗欧美成人网站| 欧美精品久久天天躁| 国产360激情盗摄全集| 麻豆国产黄色一级免费片| 国产999久久高清免费观看| 亚洲精品麻豆一区二区| 米奇亚洲国产精品思久久| 91国在线啪精品一区| 中文无码热在线视频|