clr via c# clr寄宿和AppDomain (一)

1 clr寄宿-----.net framework在windows平台的顶部允许。者意味着.net framework必须用windows能理解的技术来构建。所有托管模块和程序集文件必须使用windows PE文件格式,而且要么是windows exe文件,要么是DLL文件

2,ICLRRuntimeHost可以做以下事情
①设置宿主管理器。该诉CLR宿主想参与与涉及以下操作的决策:内存分配、线程调度/同步以及程序集加载等。宿主还可声明它想获得有关垃圾回收启动和停止以及特定操作超时的通知
②获取CLR管理器。告诉CLR阻止使用某些类/成员。另外,宿主能分辨哪些代码可以调试,哪些不可以,以及当特定事件(例如AppDomain卸载、CLR停止或者堆栈移除异常)发生时宿主应调用哪个方法
③初始化并启动CLR
④加载程序集并执行其中的代码
⑤停止CLR,阻止任何更多的托管代码在Windows进程中运行

3,AppDomain

  • 一个AppDomain不能直接访问另外一个AppDomain的代码创建的对象
  • AppDomain可以卸载,卸载一个App Domain,可以卸载当前包含的所有程序集
  • App Domain可以单独保护.
  • App Domain可以单独配置
  • 一个进程,上面运行着一个CLR COM服务器.该CLR当前管理者两个AppDomain,每个App Domain都有着自己的Loader堆,上面记录了App Domain创建以来访问过的类型,每个类型对象都有一个方法表,方法表中的每个记录项都指向Jit编译过的本机代码.
  • 每个AppDomain都加载了程序集.

3,跨越AppDomain边界访问对象.

namespace ClrFromCSharp_2_2.LearnAppDomain
{
   public class AppDomainRef
    {
        public static void Marshalling()
        {
            AppDomain adCallingTreadDomain = Thread.GetDomain();//获取当前线程运行的域
            string callingDomainName = adCallingTreadDomain.FriendlyName;
            Console.WriteLine("Default AppDomain‘s friendly name={0}", callingDomainName);
            //获取并显示我们的AppDomain中包含了Main方法的程序集
            string exeAssembly = Assembly.GetEntryAssembly().FullName;
            Console.WriteLine("Main assembly={0}", exeAssembly);
            //定义局部变量来引用一个AppDomain
            AppDomain ad2 = null;
            Console.WriteLine("\n Demo #1");
            ad2 = AppDomain.CreateDomain("AD #2", null, null);
            MarshalByRefType mbrt = null;
            mbrt = (MarshalByRefType)ad2.CreateInstanceAndUnwrap(exeAssembly, "ClrFromCSharp_2_2.LearnAppDomain.MarshalByRefType");//利用AppDomain和程序集创建对象.
            Console.WriteLine("Type={0}", mbrt.GetType());
            Console.WriteLine("Is proxy={0}", RemotingServices.IsTransparentProxy(mbrt));//
            //看起来我们像在MarshalByRefType上面调用一个方法,实则不然,我们在代理类型上调用一个方法,代理使线程切换到拥有对象的那个AppDomain,并在真实对象上调用
            //真实的方法.
            mbrt.SomeMethod();
            //卸载 AppDomain
            AppDomain.Unload(ad2);
            //mbrt 引用一个有效的代理对象,代理对象引用一个无效的AppDomain
            try
            {
                mbrt.SomeMethod();
                Console.WriteLine("Successful");

            }
            catch (AppDomainUnloadedException)
            {
                Console.WriteLine("Failed Call.");
            }
            //---Demo2---使用MarshalByValue进行跨AppDomain通信
            Console.WriteLine("\n Demo #2");
            ad2 = AppDomain.CreateDomain("AD #2", null, null);
            mbrt = (MarshalByRefType)ad2.CreateInstanceAndUnwrap(exeAssembly, "ClrFromCSharp_2_2.LearnAppDomain.MarshalByRefType");//利用AppDomain和程序集创建对象.
            MarshalByValType mbvt = mbrt.MethodWithReturn();
            Console.WriteLine("Is proxy={0}", RemotingServices.IsTransparentProxy(mbvt));//证明得到的不是一个代理对象的引用
            Console.WriteLine("Returned object created" + mbvt.ToString());
            //卸载新的AppDomain
            AppDomain.Unload(ad2);
            //mbvt引用有效的对象:卸载AppDomain没用影响
            try
            {
                Console.WriteLine("Returned object created" + mbvt.ToString());
                Console.WriteLine("Successful");
            }
            catch (AppDomainUnloadedException)
            {
                Console.WriteLine("Failed Call.");
            }
            //Demo3 :使用不可封送的对象,抛出异常
            mbrt = (MarshalByRefType)ad2.CreateInstanceAndUnwrap(exeAssembly, "ClrFromCSharp_2_2.LearnAppDomain.MarshalByRefType");//利用AppDomain和程序集创建对象.
            NonMarshalableType nmt = mbrt.MethodArgAndReturn("abc");
            //这里永远执行不到...
        }
    }
    public sealed class MarshalByRefType : MarshalByRefObject
    {
        public MarshalByRefType()
        {
            Console.WriteLine("{0} ctor running in {1}", this, GetType().ToString(), Thread.GetDomain().FriendlyName);

        }
        public void SomeMethod()
        {
            Console.WriteLine("Executing in" + Thread.GetDomain().FriendlyName);
        }
        public MarshalByValType MethodWithReturn()
        {
            Console.WriteLine("Executing in" + Thread.GetDomain().FriendlyName);
            return new MarshalByValType();
        }
        public NonMarshalableType MethodArgAndReturn(string name)
        {
            Console.WriteLine("calling from {0} to {1}", name, Thread.GetDomain().FriendlyName);
            return new NonMarshalableType();
        }
    }
    [Serializable]
    public sealed class MarshalByValType : Object
    {
        private DateTime m_creationTime = DateTime.Now;

        public MarshalByValType()
        {
            Console.WriteLine("{0} ctor running in {1},Created On {2:D}", this.GetType().ToString(), Thread.GetDomain().FriendlyName, m_creationTime);
        }
        public override string ToString()
        {
            return m_creationTime.ToLongDateString();
        }
    }
    [Serializable]
    public sealed class NonMarshalableType : Object
    {
        public NonMarshalableType()
        {
            Console.WriteLine("Executing in" + Thread.GetDomain().FriendlyName);
        }
    }

}

运行结果

Default AppDomain‘s friendly name=ClrFromCSharp_2_2.exe
Main assembly=ClrFromCSharp_2_2, Version=1.0.0.0, Culture=neutral, PublicKeyToken=11fccde6e91ad1e9

 Demo #1
ClrFromCSharp_2_2.LearnAppDomain.MarshalByRefType ctor running in ClrFromCSharp_2_2.LearnAppDomain.MarshalByRefType
Type=ClrFromCSharp_2_2.LearnAppDomain.MarshalByRefType
Is proxy=True
Executing inAD #2
Failed Call.

 Demo #2
ClrFromCSharp_2_2.LearnAppDomain.MarshalByRefType ctor running in ClrFromCSharp_2_2.LearnAppDomain.MarshalByRefType
Executing inAD #2
ClrFromCSharp_2_2.LearnAppDomain.MarshalByValType ctor running in AD #2,Created On 2020年2月13日
Is proxy=False
Returned object created2020年2月13日
Returned object created2020年2月13日
Successful

1,线程也就是方法中,可以通过Thread.Get Domain(),来获取域的信息.也可以通过System.AppDomain.CurrentDomain获取

2,FriendName:一个用于辨识的名称.

3,Assembly.GetEntryAssembly().FullName获取当前运行程序的程序集的强名称(Full Name)

4,利用AppDomain.CreateDomain()创建AppDomain

  • FriendName
  • System.Security.Policy.Evidence,传递null代表继承当前AppDomain的权限集证据.
  • System.AppDomainSetup.代表Domain的配置设置.传递Null代表使用当前AppDomain的配置

当新建了一个App Domain后,其Loader堆是空的.CLr不在这个App Domain上创建任何线程,AppDomain中也不会运行代码.除非显式调用代码.

5,利用mbrt = (MarshalByRefType)ad2.CreateInstanceAndUnwrap(exeAssembly, "ClrFromCSharp_2_2.LearnAppDomain.MarshalByRefType");//利用AppDomain和程序集创建对象.
,注意,首先,利用该函数创建一个代理对象

两个参数.一个使程序集的FullName,一个是程序集所在的类的强名称.

mbrt = (MarshalByRefType)ad2.CreateInstanceAndUnwrap(typeof(MarshalByRefType).Assembly.FullName, typeof(MarshalByRefType).FullName);//利用AppDomain和程序集创建对象.

使用这个函数时候,线程切换到AppDomain ad2,然后在上面创建类型对象(派生自MarshalByRefObject),然后,返回给实际的ad1一个引用的代理对象:

该代理对象有完全一样的实列成员(属性,事件,方法)但是,其不含实列的字段.相反,其含有

  • 字段1:指出在哪个AppDomain之中
  • 字段2:一个GCHandle,来引用真实的对象.

通过堆栈调式和Porxy代理方法可以知道,这是一个代理对象.当执行该代理对象的方法时,实际上线程切换到了ad2,并且在ad2上执行方法.

执行完毕后,其返回到原来的App Domain之中.

6,接下来,卸载了AppDomain.包括了所有程序集.并且强制执行一次垃圾回收.下一次调用实际上是调用代理上面的方法,代理发现方法的真实对象所在的APp已经卸载,抛出错误AppDomainUnloadedException异常.

7,对于派生自MarshalByRefObject派生的类的字段访问时,实际上调用System.Object的Field Getter或FieldSetter方法(利用反射)获取或者设定字段值.因此,访问字段性能很差.

8,

生存期租约.由于第二个AppDomain中的对象没有根,所以使用代理引用原始对象可以被垃圾回收.重写InitializeLifetimeService

public class MyClass : MarshalByRefObject
{
  [SecurityPermissionAttribute(SecurityAction.Demand,
                               Flags=SecurityPermissionFlag.Infrastructure)]
  public override Object InitializeLifetimeService()
  {
    ILease lease = (ILease)base.InitializeLifetimeService();
    if (lease.CurrentState == LeaseState.Initial)
    {
         lease.InitialLeaseTime = TimeSpan.FromMinutes(1);
         lease.SponsorshipTimeout = TimeSpan.FromMinutes(2);
          lease.RenewOnCallTime = TimeSpan.FromSeconds(2);
    }
      return lease;
  }
}

9,按值封送对象([serialzible],但是未实现MarshalByRefObject:

当一个对象被标识成[Serialable]时候,CLR将对象实列的字段序列化为一个字节数组,然后,CLR在目标的APPDOMAIN中反序列化对象,并且强制加载目标对象的程序集.也就是在目标AppDomain中精确复制了源对象.

10,当上述两种方法都不行时,则报错.

11,卸载AppDomain.

使用AppDomain.unload()来卸载AppDomain.

  1. 1.clr 挂起进程中执行过的托管代码的所有线程
  2. 2,LCR检查所有线程栈,查看哪些线程正在执行要卸载的App Domain中的代码,或那些线程会在某个时刻返回至要卸载的App Domain.
  3. CLR 会强迫对应的线程抛出一个ThreadAbortException(同时回复线程的执行).同时线程恢复执行.如果没有代码捕捉异常,CLR吞噬这个异常,线程会终止,但是进程会继续运行.这是很特别的一点,因为对于其他未经过处理的异常,CLR会终止进程.
  4. 3,当第二步发现所有线程都离开AppDomain后,CLR遍历堆,为所有引用了已卸载的domain创建的代理对象都设置一个标志.通知他们引用的真实对象已经不再了.现在任何代码在无效代理对象上调用方法都将抛出AppDomainUnloadedException异常
  5. 4,CLR强制垃圾回收,回收由已卸载的AppDomain创建的任何对象的内存.这些对象的Finalize方法被调用,使这些对象有机会清理他们占用资源
  6. 5,CLR恢复成语所有线程执行.Unload方法是同步的,如果10秒返回,则抛出一个CannotUnloadAppDomainException异常.

12,监视AppDomain

  • MonitoringSurvivedProcessMemorySize:当前进程所有domain分配的大小
  • MonitoringTotalAllocatedMemorySize:这个实列属性返回,实列Domain所有分配的内存大小()
  • MonitoringSurvivedMemorySize:这个实列属性返回,实列DOmain存活对象的内存大小()
  • MonitoringTotalProcessorTimer:这个实列返回实列Domain运行到当前的时间片段.
  •  public sealed class AppDomainMonitorDelta:IDisposable
        {
            private AppDomain m_appDomain;
            private TimeSpan m_thisADCpu;
            private Int64 m_thisADMemoryInUse;
            private Int64 m_thisADMemoryAllocated;
    
            static AppDomainMonitorDelta()
            {
                AppDomain.MonitoringIsEnabled = true;
            }
            public AppDomainMonitorDelta(AppDomain ad)
            {
                m_appDomain = ad ?? AppDomain.CurrentDomain;
                m_thisADCpu = m_appDomain.MonitoringTotalProcessorTime;
                m_thisADMemoryInUse = m_appDomain.MonitoringSurvivedMemorySize;
                m_thisADMemoryAllocated = m_appDomain.MonitoringTotalAllocatedMemorySize;
    
            }
            public void Dispose()
            {
                GC.Collect();
                Console.WriteLine("FriendlyName={0},cpu={1}ms", m_appDomain.FriendlyName, (m_appDomain.MonitoringTotalProcessorTime - m_thisADCpu).TotalMilliseconds);
                Console.WriteLine("Allocated {0:N0} bytes of which {1:N0} survived GCS", m_appDomain.MonitoringTotalAllocatedMemorySize - m_thisADMemoryAllocated,
                    m_appDomain.MonitoringSurvivedMemorySize - m_thisADMemoryInUse);
            }
        }
    

该类用于监视两个时间点之间AppDomain发生的变化

using(new AppDomainMonitorDelta(null))
            {
                var list = new List<object>();
                for (var x = 0; x < 2000; x++) { list.Add(new byte[10000]); }
                for (var x = 0; x < 2000; x++) { new Byte[10000].GetType(); }
                Int64 stop = Environment.TickCount + 5000;
                while (Environment.TickCount < stop) ;
            }

13,AppDomain FirstChanceException 异常通知---

  • 异常首次抛出的时候,CLR调用抛出异常的Domain的FirstChanceException回调方法.
  • 然后,CLR查找栈上任何catch块.如有处理,则正常运行,异常处理完成.如果没有,则沿着栈向上来到调用该Appdomain的Appdomain.
  • 通过序列化,反序列化,再次抛出该异常.在当前app domain中
  • 然后clr调用FirstChanceException方法.继续,第2步.
  • 如果都执行完毕还有异常没有处理,则终止进程.
  •  public class TestAppDomainFirstChanceNotify:MarshalByRefObject//创建一个引用传递的对象.
        {
            private string m_name;
            static TestAppDomainFirstChanceNotify()//在类初始化的时候,添加了当appdomain发生异常时,FirstChanceException添加事件.
            {
                Thread.GetDomain().FirstChanceException += (obj, e) =>
                  {
                      Console.WriteLine("FirstChanceCaused in {0}", Thread.GetDomain().FriendlyName);
                  };
            }
            public TestAppDomainFirstChanceNotify() : this("notify") { }
            public TestAppDomainFirstChanceNotify(string name)
            {
                m_name = name;
                Console.WriteLine("created at appdomain {0}", Thread.GetDomain().FriendlyName);//创建对象.
            }
            public void PullException(string message)//调用方法,抛出异常.
            {
                Console.WriteLine("try throw Exception at appdomain {0}", Thread.GetDomain().FriendlyName);
    
                throw new Exception(message);
    
            }
        }
    

结果:

public static void CallFirstChance()
        {
            Thread.GetDomain().FirstChanceException += FirstChangeHandler;//当当前appdomain发生第一次异常的时候,调用回调函数.
            AppDomain ad = AppDomain.CreateDomain("AD0");//创建新的app domain---ad;
            TestAppDomainFirstChanceNotify t = (TestAppDomainFirstChanceNotify)ad.CreateInstanceAndUnwrap(typeof(TestAppDomainFirstChanceNotify).Assembly.FullName, typeof(TestAppDomainFirstChanceNotify).FullName);
            try//创建代理对象
            {
                t.PullException("pull exception");//代理对象调用异常方法.
            }
            catch(Exception e)
            {
                Console.WriteLine("exception happened at {0}", Thread.GetDomain().FriendlyName);
            }

        }

created at appdomain AD0:创建对象
try throw Exception at appdomain AD0://抛出异常
FirstChanceCaused in AD0//调用ad0 中的FirstChanceException事件.
pull exception//调用当前app中的FirstChanceCaused
exception happened at ClrFromCSharp_2_2.exe//catch吃掉异常.

原文地址:https://www.cnblogs.com/frogkiller/p/12304609.html

时间: 2024-10-10 07:16:37

clr via c# clr寄宿和AppDomain (一)的相关文章

【C#进阶系列】22 CLR寄宿和AppDomain

关于寄宿和AppDomain 微软开发CLR时,将它实现成包含在一个DLL中的COM服务器. 任何Windows应用程序都能寄宿(容纳)CLR.(简单来讲,就是CLR在一个DLL中,通过引用这个DLL,可以实现包含CLR) CLR COM服务器初始化会创建一个默认AppDomain,这个AppDomain只有在进程终结时才会被销毁. 然而宿主程序还可以要求CLR创建额外的AppDomain. 因为创建进程开销很大,并且需要大量内存来虚拟化进程的地址空间. 所以人们就像可不可以在一个进程上运行多个

第22章 CLR寄宿和AppDomain

寄宿允许使任务应用程序都能利用CLR的功能.寄宿(hosting)允许使任务应用程序都能利用CLR的功能.另外,寄宿还为应用程序提供了通过编程来进行自定义和扩展能力.AppDomain允许第三方的,不受信任的代码在一个现有的进程中运行,而CLR保证数据结构.代码和安全上下文不会被滥用或破坏. 22.1 CLR寄宿 CLR Hosting(CLR 宿主)的概念:初始启动.Net Application时,Windows进程的执行和初始化跟传统的Win32程序是一样的,执行的还是非托管代码,只不过由

CLR基础,CLR运行过程,使用dos命令创建、编译、运行C#文件,查看IL代码

CLR是Common Language Runtime的缩写,是.NET程序集或可执行程序运行的一个虚拟环境.CLR用于管理托管代码,但是它本身是由非托管代码编写的,并不是一个包含了托管代码的程序集,所以不能使用IL DASM进行查看,但CLR以dll的形式位于.NET版本号文件夹内. □ C#源代码从编译到CLR运行的全过程 →编写C#源代码,以class,struct,enum,interface,delegate...的形式 →编译器把源代码编译成.dll或.exe,其中包含了一些重要信息

CLR via C# - CLR模型

博客园对markdown支持不佳,错乱移步Github IO 博文 CLR 的执行模型 模块/程序集 1.模块 托管模块组成部分 PE32/PE32+头 : PE即Portable Executable表示可执行文件 CLR头 : 包含所需的CLR版本,Main方法入口 元数据 : 元数据是一组数据表.其中的一些数据表描述了模块中定义的内容,比如类型或成员.还有一些描述了托管模块引用的内容,比如导入的类型.成员. IL代码 元数据用途 用于编译,消除了C/CPP时期对头文件的依赖 VS Inte

CLR寄宿和AppDomain

一.CLR寄宿 .net framework在windows平台的顶部允许.者意味着.net framework必须用windows能理解的技术来构建.所有托管模块和程序集文件必须使用windows PE文件格式,而且要么是windows exe文件,要么是DLL文件 1,MSCorEE.dll(垫片)①CLRCreateInstance函数在MSCorEE.dll文件中实现."垫片"的工作是决定创建哪个版本的CLR(1.0.2.0.3.0的CLR代码在MSCorWks.dll文件中:

CLR via C#读书笔记 CLR寄宿和AppDomain

寄宿 寄宿是指让其他应用程序(非托管代码)使用CLR的能力,比如自己用C++开发的窗体能创建CLR实例. 托管代码也能调用非托管代码 [DllImport("kernel32.dll")] public static extern int WinExec(string exeName, int operType); 通常会调用win32 api,但是要查文档才知道怎么定义extern方法 CLR实际上被实现为COM服务器,可以通过CoCreateInstance或CLRCreateIn

《CLR via C#》 第22章 CLR寄宿和AppDomain 跨越AppDomain边界访问对象

跨越AppDomain边界访问对象 将书中的代码(3处)将“MarshalByRefType”修改为“typeof(MarshalByRefType).FullName”,即可得到书中的输出结果: 将:MarshalByRefType mbrt = (MarshalByRefType)ad2.CreateInstanceAndUnwrap(exeAssembly,“MarshalByRefType”); 修改为:MarshalByRefType mbrt = (MarshalByRefType)

CLR via C#--------CLR的执行模式

CLR:是一个可由多种编程语言使用的"运行时". CLR的核心功能(比如 内存管理.程序集加载.安全性.异常处理.线程同步)可由面向CLR的所有语言使用. CLR是完全围绕类型展开的. 面向CLR的语言编译器: Microsoft:C++/CLI.C#.Visual Basic.F#.Iron Python 以及IL汇编器. 其他公司.学院:Ada.PAL.Caml.COBOL.LOGO.PHP... 托管模块的组成部分:PE32/PE32+头.CLR头.元数据.IL代码. IL基于栈

初入 CLR - 阅读《CLR via C#》笔记

最近买了一本书<CLR via C#>阅读了第一章 - CLR 的执行模型,对 .NET 一直提到的 CLR 和 .NET Framework 有了一个大致的了解.我理解主要体现在: ■ 各种术语有了一个大致的体会:CTS CLS 和 CLI .CLR 与 .NET Framework. IL(Intermediate Language) .CIL(Common Intermediate Language)和 托管代码(Managed Code). ■ 用 C# 写完一个程序被编译成应用程序后