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.clr 挂起进程中执行过的托管代码的所有线程
- 2,LCR检查所有线程栈,查看哪些线程正在执行要卸载的App Domain中的代码,或那些线程会在某个时刻返回至要卸载的App Domain.
- CLR 会强迫对应的线程抛出一个ThreadAbortException(同时回复线程的执行).同时线程恢复执行.如果没有代码捕捉异常,CLR吞噬这个异常,线程会终止,但是进程会继续运行.这是很特别的一点,因为对于其他未经过处理的异常,CLR会终止进程.
- 3,当第二步发现所有线程都离开AppDomain后,CLR遍历堆,为所有引用了已卸载的domain创建的代理对象都设置一个标志.通知他们引用的真实对象已经不再了.现在任何代码在无效代理对象上调用方法都将抛出AppDomainUnloadedException异常
- 4,CLR强制垃圾回收,回收由已卸载的AppDomain创建的任何对象的内存.这些对象的Finalize方法被调用,使这些对象有机会清理他们占用资源
- 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吃掉异常.
clr via c# clr寄宿和AppDomain (一)的更多相关文章
- 【C#进阶系列】22 CLR寄宿和AppDomain
关于寄宿和AppDomain 微软开发CLR时,将它实现成包含在一个DLL中的COM服务器. 任何Windows应用程序都能寄宿(容纳)CLR.(简单来讲,就是CLR在一个DLL中,通过引用这个DLL ...
- 第二十二章 CLR寄宿和AppDomain
1. 概念解析 CLR Hosting(CLR 宿主):初始启动.Net Application时,Windows进程的执行和初始化跟传统的Win32程序是一样的,执行的还是非托管代码,只不过由于PE ...
- CLR寄宿和AppDomain
一.CLR寄宿 .net framework在windows平台的顶部允许.者意味着.net framework必须用windows能理解的技术来构建.所有托管模块和程序集文件必须使用windows ...
- 第22章 CLR寄宿和AppDomain
22.1 CLR寄宿 CLR Hosting(CLR 宿主)的概念:初始启动.Net Application时,Windows进程的执行和初始化跟传统的Win32程序是一样的,执行的还是非托管代码,只 ...
- 重温CLR(十六) CLR寄宿和AppDomain
寄宿(hosting)使任何应用程序都能利用clr的功能.特别要指出的是,它使现有应用程序至少能部分使用托管代码编写.另外,寄宿还为应用程序提供了通过编程来进行自定义和扩展的能力. 允许可扩展性意味着 ...
- 【CLR】解析CLR的托管堆和垃圾回收
目录结构: contents structure [+] 为什么使用托管堆 从托管堆中分配资源 托管堆中的垃圾回收 垃圾回收算法 代 垃圾回收模式 垃圾回收触发条件 强制垃圾回收 监视内存 对包装了本 ...
- CLR基础,CLR运行过程,使用dos命令创建、编译、运行C#文件,查看IL代码
CLR是Common Language Runtime的缩写,是.NET程序集或可执行程序运行的一个虚拟环境.CLR用于管理托管代码,但是它本身是由非托管代码编写的,并不是一个包含了托管代码的程序集, ...
- CLR via C#--------CLR的执行模式
CLR:是一个可由多种编程语言使用的“运行时”. CLR的核心功能(比如 内存管理.程序集加载.安全性.异常处理.线程同步)可由面向CLR的所有语言使用. CLR是完全围绕类型展开的. 面向CLR的语 ...
- CLR via C# - CLR模型
博客园对markdown支持不佳,错乱移步Github IO 博文 CLR 的执行模型 模块/程序集 1.模块 托管模块组成部分 PE32/PE32+头 : PE即Portable Executabl ...
随机推荐
- 到头来还是逃不开Java - Java13程序基础
java程序基础 没有特殊说明,我的所有学习笔记都是从廖老师那里摘抄过来的,侵删 引言 兜兜转转到了大四,学过了C,C++,C#,Java,Python,学一门丢一门,到了最后还是要把Java捡起来. ...
- [bzoj3143] [洛谷P3232] [HNOI2013] 游走
Description 一个无向连通图,顶点从1编号到N,边从1编号到M. 小Z在该图上进行随机游走,初始时小Z在1号顶点,每一步小Z以相等的概率随机选 择当前顶点的某条边,沿着这条边走到下一个顶点, ...
- 常用API_1
API API(Application Programming Interface),应用程序编程接口.Java API是一本程序员的 字典 ,是JDK中提供给 我们使用的类的说明文档.这些类将底层的 ...
- TryCatchFinallyReturn
public class TryCatchFinallyReturnTest { public int test(){ try { int i=1; int j=2/i; return 1; }cat ...
- 源码的说明 ASP.NET MVC 5框架揭秘.zip
第1章 S101 MVP(SC)模式中Presenter与View之间的交互 S102 迷你版的ASP.NET MVC框架 第2章 S201 通过路由实现请求地址与.aspx页面的映射 S202 基本 ...
- selenium,CSS定位法应用
如图,下载按钮 查看其元素,是无法直接定位的,通过xpath也无法定位,转为firepath获取的CSS也是无效的#downloadItems>a 但是为经过firepath,直接在html下复 ...
- Day2-Python3基础-文件操作
1. 字符编码与转码 需知: 1.在python3默认编码是unicode 2.unicode 分为 utf-32(占4个字节),utf-16(占两个字节),utf-8(占1-4个字节), so ut ...
- HDU-6185-Covering(推递推式+矩阵快速幂)
Covering Time Limit: 5000/2500 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)Total Su ...
- JDK Proxy和CGLIB Proxy
动态代理在Java中有着广泛的应用,比如Spring AOP,Hibernate数据查询.测试框架的后端mock.RPC,Java注解对象获取等.静态代理的代理关系在编译时就确定了,而动态代理的代理关 ...
- flask使用blinker信号机制解耦业务代码解决ImportError: cannot import name 'app',以异步发送邮件为例
百度了大半天,不知道怎么搞,直到学习了blinker才想到解决办法,因为之前写java都是文件分开的, 所以发送邮件业务代码也放到view里面,但是异步线程需要使用app,蛋疼的是其他模块不能从app ...