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 ...
随机推荐
- 机器学习环境配置系列五之keras2
keras一个大坑就是配置文件的问题,网上会给很多的误导,让我走了很多弯路. 1.安装keras2 conda install keras 2.环境配置 echo ‘{ "epsilon&q ...
- 爬虫之协程,selenium
1.什么是代理?代理和爬虫之间的关联是什么? 2.在requests的get和post方法常用的参数有哪些?分别有什么作用?(四个参数) - url headers parmas/data proxi ...
- linux--->配置lamp环境(centos7 最小版)
这篇博客写的很全,按照顺序敲代码即可 参考:https://www.cnblogs.com/me80/p/7218883.html
- IDEA更换banner(娱乐专用)
1.佛祖保佑 永无bug _ooOoo_ o8888888o 88" . "88 (| -_- |) O\ = /O ____/`---'\____ .' \\| |// `. / ...
- shiro盐值加密并验证
在数据表中存的密码不应该是123456,而应该是123456加密之后的字符串,而且还要求这个加密算法是不可逆的,即由加密后的字符串不能反推回来原来的密码,如果能反推回来那这个加密是没有意义的.著名的加 ...
- ATL的GUI程序设计(4)
第四章 对话框和控件 对于Win32 GUI的程序设计来说,其实大部分的情况下我们都不需要自己进行窗口类的设计,而是可以使用Win32中与用户交互的标准方式--对话框(Dialog Box).我们可以 ...
- ATL的GUI程序设计(前言)
前言 也许,你是一个顽固的SDK簇拥者: 也许,你对MFC抱着无比排斥的态度,甚至像我一样对它几乎一无所知: 也许,你符合上面两条,而且正在寻求着一种出路: 也许,你找到了一条出路--WTL,但是仍然 ...
- [python之路]学习路线
python基础 #为什么要学python?python在知名公司广泛应用,谷歌.cia.nasa.youtobe.dropbox.instagram.facebook.redhat.豆瓣.知乎.搜狐 ...
- (二)MyBatis延迟加载,一级缓存,二级缓存
延迟加载配置: 什么时候用延迟加载?比如现在有班级和学生表,一对多关系,你可能只需要班级的信息,而不需要该班级学生的信息,这时候可以进行配置,让查询时先查询到班级的信息,在之后需要学生信息时候,再进行 ...
- 题解 bzoj3688【折线统计】
考虑 \(dp\) . 首先把所有节点按 \(x\) 从小到大排序是很有必要的. 记 f[i][j][0] 表示满足以第 \(i\) 个节点做折线结尾,选取的点集 \(S\) 满足 \(f(S)=j\ ...