什么是AppDomain?
我们都知道windows进程,它起到应用程序隔离的作用,带来的好处是,当某个进程发生错误的时候,不会影响其他的进程,系统也不会受到影响。但是,创建windows进程的代价是很大的。.net推出AppDomain的作用也是隔离,如果能确保应用程序都是安全代码(managed code),那么appdomain其实就可以起到进程的作用。每个appdomain里有自己的堆,负责自己内部对象的创建和销毁,对象一般不能夸appdomain访问。
当应用程序启动,CLR加载后,会创建一个appdomain(默认的,它知道进程结束才会unload),程序集会在此appdomain中加载,运行。
Appdomain,程序集,进程的关系如下图所示:
这里有个问题,即同样的assembly会在不同的appdomain中使用,会造成资源的浪费,所以出现了domain-neutral assemblies,也就是说,这些assemblies与appdomain无关,CLR负责管理,对象的创建,销毁都由CLR负责,当然,它们也只有等到CLR结束,也就是进程结束时才能unload。
跨AppDomain访问对象
CLR Via C#书中第22章的例子,很好的解释了如何跨appdomain对象访问。
它会建一个新的appdomain,在这里会创建(返回)3种类型的对象,1个是继承自MarshaByRefObject类型的对象,1个是标注了[Serializable]的类型的对象,另一个是普通类型对象,对应有3个#demo。
#demo1
在新的appdomain中动态创建继承自MarshaByRefObject类型的对象,然后在默认的appdomain中使用它的方法:
(1) 在新appdomain中create的对象,返回给原appdomain,实际上返回给原appdomain的是一个代理,也就是说mbrt变量指向的是一个proxy,而非新appdomain中的instance,所以IsTransparentProxy返回true。
(2) 在代理上执行方法,显然是在远程执行。
(3) 当新的appdomain卸载后,代理找不到原始对象,抛异常。
#demo2
在新的appdomain中动态创建标有[Serializable]的类型的对象,然后再默认的appdomain中使用
(1) 在新的appdomain创建的,返回原appdomain的,不会是代理,而是新appdomain中创建的对象的copy,在跨appdomain边界的时候序列化成字节流,回到原appdomain时,反序列化回来的,所以IsTransparentProxy返回false。
(2) 由于mbvt指向的对象不是代理对象,所以在本地(本appdomain)执行。
(3) 由于在本地执行,所以尽管新的appdomain被unload了,仍然可以继续执行。
#demo3
在新的appdomain中动态创建普通类型对象,既没有继承自MarshalByRefObject,也没有标注[Serializable],然后再默认的appdomain中使用,这个时候,由于新appdomain发现,既不能按Marshal by ref封送,就按Marshal by value封送,但是却不能序列化,所以只有跑序列化失败的异常了:
结论:跨appdomain对象访问有2种方式:by ref, by value,
(1) by ref要求对象继承自MarshalByRefObject,并且在目的appdomain中使用的是代理。
(2) by value要求对象标注[Serializable],即能被序列化,那么在目的appdomain中,使用的就是原对象的副本了。