一、走进.NET AppDomain

天哪,.NET Framwork的CLR真是巧妙呢!随着越来越多的对.Net底层编程的了解,一些诸如架构,处理过程的复杂难懂的细节完全的让我叹服,所以呢,再次错过我们之前忽视的细节只美事不可能的了,有个与CLR肩并肩协同工作的一个核心组件,叫做AppDomain,作为.NET Framework的一部分,AppDomain是一个微软引入的非常酷的概念。

为了更好的理解.Net的AppDomain和AppDomian是如何影响我们创建并在其上工作的,还是从头说起比较好,那么我让我们从在应用程序中点击一个按钮开始,无论何时我们启动一个应用程序,我们实际上启动了一个Win32的进程,并且在这个进程中运行我们的程序。这些进程使用那些诸如内存,对象,内核还有等等的一些资源,任何一个Win32进程包含至少一个线程(越来越多,后来就是多线程了),并且如果我们运行其他的任务或者从我们的应用程序中打开其他的应用程序,那么这些任务都会属于运行多线程集合的我们的那个Win32进程。

Win32进程有一个特点,那就是他和虚拟边界很相似,在进程内通信很容易,但是想要在某种水平上与Win32进程外通信就要收到诸多限制,为了与其他的Win32进程通信,我们需要一些特别的工作机制,因为有一些安全上下文需要考虑(security contexts),并且还需要考虑在特定系统中,Win32进程能做什么和不能做什么。

那么谁负责运行一个进程?一个进程成功的运行都需要涉及到哪些因素呢?进程的执行以及进程中我们的代码的运行都是在域(domain)和操作系统的权限下的,在维护一个活动状态的进程时,操作系统不得不处理许多负责的情况和要点,让我们来看一下实际情形吧。

考虑一个如下的情形,我们的一个服务器内寄宿着很多个web应用程序,或者说,我们可能不得不在我们系统中运行一堆Windows应用程序,现在,瞧瞧我们正在处理什么,瞧瞧我们得给这些应用程序什么吧,那就是资源!资源!更多得资源。我们的资源(内存)时非常安规的,并且在我们为不同客户同时运行程序的时候,安全问题开始出现了,所以迫切需要我们彻底禁止任何方式的在这些应用程序进程间通信(然而,这总是和需求有冲突),这样做会导致经常性的崩溃,经常一个进程用光了被另一个进程申请来的内存,然后就导致崩溃。不管怎样,我们的进程很糟糕,没啥新鲜的,这些频繁的运行时错误通常是由低效率的内存使用所引发的内存泄露,对象空引用,内存越界等等造成的。所以,大家越来越意识到,在一个多用户的环境下创建、运行、维护进程是非常非常昂贵的。因此,运行一大堆进程并不是个好主意,因为他们递增适应性不怎么好。

在这种情况下,一种非常精巧的解决方案应运而生,在同一个宿主进程下运行多个应用程序可以让我们使用更少的资源,这甚至会导致更快的执行过速度,但是有另一个每天都在发生的场景:一旦一个应用程序崩溃,所有其他的在这个相同进程中的应用程序就会像一副纸牌一样全部玩儿完!是的,我们说的就是这样的多米诺骨牌效应。

那现在怎么办呢?

使用.Net AppDomain吧。这个概念非常巧妙,有新意。配得上诺贝尔奖,引入Application Domain的主要目的,就是将我们的应用程序与其他的应用程序隔离开,Application Domains运行在一个单独的Win32进程里。与我们刚刚谈到的解决方案相同,通过Application Domain内运行我们的应用程序,来限制由内存泄漏引起的错误和崩溃。

因此,我们在一个应用程序域内运行应用程序,并且我们在单个Win32进程中同时运行多个应用程序域,借着CLR的运行托管代码的能力,我们可以进一步的减少泄露和崩溃(还要感谢CLR的垃圾收集器),在同一个应用程序域内对象之间直接通信,而存活在不同的应用程序域内中的对象,如果想要彼此通信的话,就要通过相互拷贝对象或者通过用于消息互换的代理了(通过引用)。

这就是Application Domain的关键之处,但是还有更多,Application Domain相当于在单Win32进程内运行的一个精巧的轻量级的进程,实际上,如同我们上面所说,我们可以在一个Win32进程中运行多个Application Domain,另一个AppDomain的优势就是,我们可以通过宿主(比如说Asp.Net)来干掉AppDomain,而不会影响到其他的正存在于那个进程中的AppDomain,所以,我们在Application Domain中的工作是独立的,更进一步地,我们可以通过销毁AppDomain来卸载掉加载那个AppDomain中地对象,神奇的.Net运行时通过接管对内存的控制来强制的使用AppDomain分隔机制,所以Win32进程中的AppDomain里使用的所有内存都由.Net运行时管理,这样我们就避免了刚开头时提及的,所有的诸如一个应用程序访问另一个应用程序的内存,之类的问题。也就避免了由崩溃引发的运行时错误问题,因此,我们实际上应用了一个安全层,隔离了当前的应用程序,与其他的应用程序分开,实际上讲,我们在创建类似与运行着的web services一样的应用程序时,Application Domain在其中扮演着一个安全方面和基础方面的关键角色。

二、AppDomain理解

为了保证代码的健壮性CLR希望不同服务功能的代码之间相互隔离,这种隔离可以创建多个进程来实现,但操作系统中创建进程是即耗时又耗资源的一件事,所以在CLR中引入了AppDomain的概念。AppDomain主要是用来实现同一个进程中的各AppDomain之间的隔离,AppDomain可以用以下特征来描述他的全貌:

1、AppDomain概念并不存在于操作系统中,而只存在于.Net中,并且AppDomain不可脱离进程单独存在,他是属于某一个CLR或寄宿着CLR的进程中的。

2、一个进程中可以有多个AppDomain,并且每个之间相互隔离(只保证安全代码的隔离,不安全代码并不能保证),因此可以理解为AppDomain是.Net进程中的“进程”,在一个AppDomain中创建的对象只属于本AppDomain,多个AppDomain之间的对象不能够相互访问,除非遵循CLR的一些规则。

3、.Net程序启动时,在进程中创建一个默认的AppDomain,入口代码将运行于此AppDomain,默认应用程序域只有在进程终止时才会被销毁,如果主动调用Unload去卸载默认应用程序域,会抛出一个CannotUnloadAppDomainException。

4、每个AppDomain都单独的加载程序集,这意味着在A应用程序域中加载了的程序集,并不一定在B应用程序域中也被加载了。每个AppDomain有单独的Loader堆,相互不影响,每个Loader推都记录了自AppDomain建立以后所访问过的类型,Loader堆中的每个类型对象都有一个方法表,这些放发表指向已经被JIT编译过的本地代码(前提是这些方法是已经被至少执行过一次的)。因为AppDomain是相互隔离的,所以相同的一个类中的方法,在A应用程序域中被JIT编译过的,但不一定在B应用程序域中也是被JIT编译过的,方法是否被JIT编译过取决于这些方法是否在本地的AppDomain被调用过。

5、当AppDomain被卸载时,此AppDomain中的程序集会被卸载,因为每个AppDomain加载的程序集都是独立的,所以每个应用程序域被卸载并不会影响其他的AppDomain中加载的程序集,另外本AppDomain的Loader堆也会被回收,每个程序域中的Loader堆时独立的,所以也不会影响到其他程序域中的Loader堆,因为Loader堆也是独立的(静态字段时存在于类型对象上的),所以一个类型中静态字段在不同应用程序域中也是不同的存在,所以静态字段也是不被影响的,唯一受影响的是线程,因为线程可以跨越应用程序域访问不同的应用程序域上的代码,后面我们会介绍当卸载一个应用程序域时如何对线程做处理。

6、有一种程序集可以被多个AppDomain使用,这种程序集叫做“AppDomain中立”的程序集,比如MSCorLib.dll,该程序集包含了System.Object、System.Int32以及其他的与.Net Framework比不可分的类型,这个程序集在CLR初始化时会自动加载,JIT会为这些程序集创建一个特殊的Loader堆,并且程序集中的方法被编译成本地代码可被所有AppDomain共享,这种程序集不可被卸载,只有当进程结束时这种程序集才会被卸载。

好了,我们来看看如何创建一个基本的应用程序域吧。

实际上,.Net Framework提供了一个漂亮的基类,他存在于System命名空间下,通过他我们可以显示的创建一个AppDomain,在我们的应用程序中,继承System.MarshalByRefObject基类,我们可以创建可以在不同应用程序域间通信的对象。

看看这个吧。一个使用C#创建AppDomain的应用程序,我们使用的是Windows控制台程序

     class Program
{
static void Main()
{
// Get and display the friendly name of the default AppDomain.
string callingDomainName = Thread.GetDomain().FriendlyName;
Console.WriteLine(callingDomainName); // Get and display the full name of the EXE assembly.
string exeAssembly = Assembly.GetEntryAssembly().FullName;
Console.WriteLine(exeAssembly); // Construct and initialize settings for a second AppDomain.
AppDomainSetup ads = new AppDomainSetup();
ads.ApplicationBase =
System.Environment.CurrentDirectory;
ads.DisallowBindingRedirects = false;
ads.DisallowCodeDownload = true;
ads.ConfigurationFile =
AppDomain.CurrentDomain.SetupInformation.ConfigurationFile; // Create the second AppDomain.
AppDomain ad2 = AppDomain.CreateDomain("AD #2", null, ads); // Create an instance of MarshalbyRefType in the second AppDomain.
// A proxy to the object is returned.
MarshalByRefType mbrt =
(MarshalByRefType)ad2.CreateInstanceAndUnwrap(
exeAssembly,
typeof(MarshalByRefType).FullName
); // Call a method on the object via the proxy, passing the
// default AppDomain's friendly name in as a parameter.
mbrt.SomeMethod(callingDomainName); // Unload the second AppDomain. This deletes its object and
// invalidates the proxy object.
AppDomain.Unload(ad2);
try
{
// Call the method again. Note that this time it fails
// because the second AppDomain was unloaded.
mbrt.SomeMethod(callingDomainName);
Console.WriteLine("Sucessful call.");
}
catch (AppDomainUnloadedException)
{
Console.WriteLine("Failed call; this is expected.");
} Console.ReadKey();
}
} public class MarshalByRefType : MarshalByRefObject
{
// Call this method via a proxy.
public void SomeMethod(string callingDomainName)
{
// Get this AppDomain's settings and display some of them.
AppDomainSetup ads = AppDomain.CurrentDomain.SetupInformation;
Console.WriteLine("AppName={0}, AppBase={1}, ConfigFile={2}",
ads.ApplicationName,
ads.ApplicationBase,
ads.ConfigurationFile
); // Display the name of the calling AppDomain and the name
// of the second domain.
// NOTE: The application's thread has transitioned between
// AppDomains.
Console.WriteLine("Calling from '{0}' to '{1}'.",
callingDomainName,
Thread.GetDomain().FriendlyName
);
}
}

ok,AppDomain就先介绍到这里,这真是一个巧妙的设计呢。

浅谈.NET中AppDomain的理解的更多相关文章

  1. 浅谈js中继承的理解和实现

    一.前言 java.C#等正统面向对象语言都会提供类似extend之类的处理类的继承的方法,而javascript并没有提供专门的方法用于继承,在javascript中使用继承需要一点技巧.js中实例 ...

  2. 浅谈 .NET 中的对象引用、非托管指针和托管指针 理解C#中的闭包

    浅谈 .NET 中的对象引用.非托管指针和托管指针   目录 前言 一.对象引用 二.值传递和引用传递 三.初识托管指针和非托管指针 四.非托管指针 1.非托管指针不能指向对象引用 2.类成员指针 五 ...

  3. 浅谈JS中的!=、== 、!==、===的用法和区别 JS中Null与Undefined的区别 读取XML文件 获取路径的方式 C#中Cookie,Session,Application的用法与区别? c#反射 抽象工厂

    浅谈JS中的!=.== .!==.===的用法和区别   var num = 1;     var str = '1';     var test = 1;     test == num  //tr ...

  4. 浅谈Java中的equals和==(转)

    浅谈Java中的equals和== 在初学Java时,可能会经常碰到下面的代码: 1 String str1 = new String("hello"); 2 String str ...

  5. 浅谈Linux中的信号处理机制(二)

    首先谢谢 @小尧弟 这位朋友对我昨天夜里写的一篇<浅谈Linux中的信号处理机制(一)>的指正,之前的题目我用的“浅析”一词,给人一种要剖析内核的感觉.本人自知功力不够,尚且不能对着Lin ...

  6. 浅谈Java中的对象和引用

    浅谈Java中的对象和对象引用 在Java中,有一组名词经常一起出现,它们就是“对象和对象引用”,很多朋友在初学Java的时候可能经常会混淆这2个概念,觉得它们是一回事,事实上则不然.今天我们就来一起 ...

  7. 浅谈Java中的equals和==

    浅谈Java中的equals和== 在初学Java时,可能会经常碰到下面的代码: String str1 = new String("hello"); String str2 = ...

  8. 浅谈JavaScript中的闭包

    浅谈JavaScript中的闭包 在JavaScript中,闭包是指这样一个函数:它有权访问另一个函数作用域中的变量. 创建一个闭包的常用的方式:在一个函数内部创建另一个函数. 比如: functio ...

  9. 【转】浅谈Java中的hashcode方法(这个demo可以多看看)

    浅谈Java中的hashcode方法 哈希表这个数据结构想必大多数人都不陌生,而且在很多地方都会利用到hash表来提高查找效率.在Java的Object类中有一个方法: public native i ...

随机推荐

  1. 小白学Java:迭代器原来是这么回事

    目录 小白学Java:迭代器原来是这么回事 迭代器概述 迭代器设计模式 Iterator定义的方法 迭代器:统一方式 Iterator的总结 小白学Java:迭代器原来是这么回事 前文传送门:Enum ...

  2. 【UEFI】---BIOS中对Guid的使用以及Lib函数的使用总结

    ---恢复内容开始--- BIOS发展至今传统的汇编实现早已被抛弃,UEFI作为目前一套主流的标准定义接口,被广泛使用.之前被一些有关GUID和一些Lib函数的使用以及跨Pkg调用给折腾的不行,每次改 ...

  3. python中常⽤的excel模块库

    python中常用的excel模块库&安装方法 openpyxl openpyxl是⼀个Python库,用于读取/写⼊Excel 2010 xlsx / xlsm / xltx / xltm⽂ ...

  4. CQBZOJ 【重庆市NOIP模拟赛】避难向导

    题目描述 "特大新闻,特大新闻!全国爆发了一种极其可怕的病毒,已经开始在各个城市 中传播开来!全国陷入了巨大的危机!大量居民陷入恐慌,想要逃到其它城市以 避难!经调查显示,该病毒来自于C 市 ...

  5. swoole(PHP异步网络通信引擎)的结构和运行流程

    swoole结构说明和运行流程 主要分为三个部分: 1.Master:swoole的主进程 处理swoole核心的事件驱动, 它包含多个线程(蓝色Reactor), 所有事件的监听都在Reactor实 ...

  6. HLS协议

    今天来介绍一下HLS协议,这个协议是由苹果公司提出并推广开来的.来一段维基百科的定义. HTTP Live Streaming(缩写是HLS)是一个由苹果公司提出的基于HTTP的流媒体网络传输协议.是 ...

  7. 深入Java类加载全流程,值得你收藏

    先测试一番,全对的就走人 //题目一 class Parent1{ public static String parent1 = "hello parent1"; static { ...

  8. SSM 配置文件 分析

    spring 配置文件(主要整合的是spring 和 mybatis 的配置文件) 问题: 两者之间没有整合在一起的时候是怎么样的 spring配置文件:    Spring配置文件是用于指导Spri ...

  9. IO博客专栏

    1. IO概览 2. 字符流与字节流的区别

  10. Spring-data-Jpa项目搭建

    传送门:Spring Data 学习   Spring Data 开发环境搭建   Spring-data-jpa详解 简介 Spring Data是什么    Spring Data是一个用于简化数 ...