应用程序域Application domains

操作系统和运行时环境通常会在应用程序间提供某种形式的隔离。Operating systems and runtime environments typically provide some form of isolation between applications. 例如,Windows 使用进程来隔离应用程序。For example, Windows uses processes to isolate applications. 为确保在一个应用程序中运行的代码不会对其他不相关的应用程序产生不良影响,这种隔离是必需的。This isolation is necessary to ensure that code running in one application cannot adversely affect other, unrelated applications.

应用程序域为安全性、可靠性、版本控制以及卸载程序集提供了隔离边界。Application domains provide an isolation boundary for security, reliability, and versioning, and for unloading assemblies. 应用程序域通常由运行时宿主创建,运行时宿主负责在运行应用程序之前引导公共语言运行时。Application domains are typically created by runtime hosts, which are responsible for bootstrapping the common language runtime before an application is run.

隔离应用程序的优点The benefits of isolating applications

以前使用进程边界来隔离在同一台计算机上运行的应用程序。Historically, process boundaries have been used to isolate applications running on the same computer. 每一个应用程序被加载到单独的进程中,这样就将该应用程序与在同一台计算机上运行的其他应用程序相隔离。Each application is loaded into a separate process, which isolates the application from other applications running on the same computer.

隔离这些应用程序的原因在于内存地址是与进程相关的;在目标进程中,不能通过任何有意义的方式使用从一个进程传递到另一个进程的内存指针。The applications are isolated because memory addresses are process-relative; a memory pointer passed from one process to another cannot be used in any meaningful way in the target process. 此外,您不能在两个进程间进行直接调用。In addition, you cannot make direct calls between two processes. 您必须代之以使用代理,它提供一定程度的间接性。Instead, you must use proxies, which provide a level of indirection.

托管代码必须先通过一个验证过程,然后才能运行(除非管理员已授权跳过该验证)。Managed code must be passed through a verification process before it can be run (unless the administrator has granted permission to skip the verification). 此验证过程将验证以下内容:这些代码是否会尝试访问无效的内存地址?是否会尝试执行某些导致进程(该代码运行时所在的进程)无法正常进行的其他操作?The verification process determines whether the code can attempt to access invalid memory addresses or perform some other action that could cause the process in which it is running to fail to operate properly. 通过此验证测试的代码将被认为是类型安全的。Code that passes the verification test is said to be type-safe. 由于公共语言运行时能够验证代码是否为类型安全的代码,所以它可以提供与进程边界一样大的隔离级别,而其性能开销则要低得多。The ability to verify code as type-safe enables the common language runtime to provide as great a level of isolation as the process boundary, at a much lower performance cost.

应用程序域提供了一个更安全、用途更广的处理单元,公共语言运行时可使用该单元提供应用程序之间的隔离。Application domains provide a more secure and versatile unit of processing that the common language runtime can use to provide isolation between applications. 您可以在具有同等隔离级别(存在于单独的进程中)的单个进程中运行几个应用程序域,而不会造成进程间调用或进程间切换等方面的额外开销。You can run several application domains in a single process with the same level of isolation that would exist in separate processes, but without incurring the additional overhead of making cross-process calls or switching between processes. 在一个进程内运行多个应用程序的能力显著增强了服务器的可伸缩性。The ability to run multiple applications within a single process dramatically increases server scalability.

隔离应用程序对于应用程序安全也是十分重要的。Isolating applications is also important for application security. 例如,您可以在单个浏览器进程中运行几个 Web 应用程序中的控件,同时使这些控件不能访问彼此的数据和资源。For example, you can run controls from several Web applications in a single browser process in such a way that the controls cannot access each other's data and resources.

应用程序域所提供的隔离具有以下优点:The isolation provided by application domains has the following benefits:

  • 在一个应用程序中出现的错误不会影响其他应用程序。Faults in one application cannot affect other applications. 因为类型安全的代码不会导致内存错误,所以使用应用程序域可以确保在一个域中运行的代码不会影响进程中的其他应用程序。Because type-safe code cannot cause memory faults, using application domains ensures that code running in one domain cannot affect other applications in the process.

  • 能够在不停止整个进程的情况下停止单个应用程序。Individual applications can be stopped without stopping the entire process. 使用应用程序域使您可以卸载在单个应用程序中运行的代码。Using application domains enables you to unload the code running in a single application.

    备注

    不能卸载单个程序集或类型。You cannot unload individual assemblies or types. 只能卸载整个域。Only a complete domain can be unloaded.

  • 在一个应用程序中运行的代码不能直接访问其他应用程序中的代码或资源。Code running in one application cannot directly access code or resources from another application. 为了强制实施此隔离,公共语言运行时禁止在不同应用程序域中的对象之间进行直接调用。The common language runtime enforces this isolation by preventing direct calls between objects in different application domains. 要在各域之间传递对象,可以复制这些对象,或通过代理访问这些对象。Objects that pass between domains are either copied or accessed by proxy. 如果复制对象,那么对该对象的调用为本地调用。If the object is copied, the call to the object is local. 也就是说,调用方和被引用的对象位于同一应用程序域中。That is, both the caller and the object being referenced are in the same application domain. 如果通过代理访问对象,那么对该对象的调用为远程调用。If the object is accessed through a proxy, the call to the object is remote. 在此情况下,调用方和被引用的对象位于不同的应用程序域中。In this case, the caller and the object being referenced are in different application domains. 域间调用所采用的远程调用基础结构与两个进程间的调用或两台计算机间的调用的基础结构相同。Cross-domain calls use the same remote call infrastructure as calls between two processes or between two machines. 因此,被引用的对象的元数据必须对于两个应用程序域均可用,以便用 JIT 正确编译该方法调用。As such, the metadata for the object being referenced must be available to both application domains to allow the method call to be JIT-compiled properly. 如果调用域对被调用对象的元数据没有访问权,则编译可能失败,并引发类型为 FileNotFoundException 的异常。If the calling domain does not have access to the metadata for the object being called, the compilation might fail with an exception of type FileNotFoundException. 有关详细信息,请参阅 Remote ObjectsFor more information, see Remote Objects. 确定如何跨域访问对象的机制是由该对象决定的。The mechanism for determining how objects can be accessed across domains is determined by the object. 有关更多信息,请参见System.MarshalByRefObjectFor more information, see System.MarshalByRefObject.

  • 代码行为的作用范围由它运行所在的应用程序决定。The behavior of code is scoped by the application in which it runs. 换言之,应用程序域将提供应用程序版本策略等配置设置、它所访问的任意远程程序集的位置,以及加载到该域中的程序集的位置信息。In other words, the application domain provides configuration settings such as application version policies, the location of any remote assemblies it accesses, and information about where to locate assemblies that are loaded into the domain.

  • 向代码授予的权限可以由代码运行所在的应用程序域来控制。Permissions granted to code can be controlled by the application domain in which the code is running.

应用程序域和程序集Application domains and assemblies

本节描述应用程序域和程序集之间的关系。This section describes the relationship between application domains and assemblies. 在可以执行程序集中所包含的代码之前,必须将程序集加载到应用程序域中。You must load an assembly into an application domain before you can execute the code it contains. 运行普通的应用程序会导致将几个程序集加载到一个应用程序域中。Running a typical application causes several assemblies to be loaded into an application domain.

程序集的加载方式决定其实时 (JIT) 编译代码是否可以在进程中由多个应用程序域共享,以及该程序集是否可以从进程中卸载。The way an assembly is loaded determines whether its just-in-time (JIT) compiled code can be shared by multiple application domains in the process, and whether the assembly can be unloaded from the process.

  • 如果程序集是以非特定于域的形式进行加载,则共享相同安全授权集的所有应用程序域都可以共享相同的 JIT 编译代码,从而减少应用程序所需的内存。If an assembly is loaded domain-neutral, all application domains that share the same security grant set can share the same JIT-compiled code, which reduces the memory required by the application. 但是,程序集则永远不能从进程中卸载。However, the assembly can never be unloaded from the process.

  • 如果程序集不是以非特定于域的形式进行加载,则它必须在加载的每个应用程序域中都是 JIT 编译的。If an assembly is not loaded domain-neutral, it must be JIT-compiled in every application domain in which it is loaded. 但是,通过卸载程序集加载的所有应用程序域,可以从进程中卸载程序集。However, the assembly can be unloaded from the process by unloading all the application domains in which it is loaded.

运行时宿主决定在将运行时加载到进程中时是否以非特定于域的形式加载程序集。The runtime host determines whether to load assemblies as domain-neutral when it loads the runtime into a process. 对于托管应用程序,将 LoaderOptimizationAttribute 特性应用于进程的入口点方法,并从关联的 LoaderOptimization 枚举指定一个值。For managed applications, apply the LoaderOptimizationAttribute attribute to the entry-point method for the process, and specify a value from the associated LoaderOptimization enumeration. 对于托管公共语言运行时的非托管应用程序,在调用 CorBindToRuntimeEx 函数方法时,指定适当的标志。For unmanaged applications that host the common language runtime, specify the appropriate flag when you call the CorBindToRuntimeEx Function method.

有三个选项用于加载非特定于域的程序集:There are three options for loading domain-neutral assemblies:

  • LoaderOptimization.SingleDomain 不以非特定于域的形式加载任何程序集(Mscorlib 除外,它始终以非特定于域的形式加载)。LoaderOptimization.SingleDomain loads no assemblies as domain-neutral, except Mscorlib, which is always loaded domain-neutral. 此设置称作单域,因为它通常用在宿主只运行进程中的单个应用程序时。This setting is called single domain because it is commonly used when the host is running only a single application in the process.

  • LoaderOptimization.MultiDomain 以非特定于域的形式加载所有程序集。LoaderOptimization.MultiDomain loads all assemblies as domain-neutral. 此设置用于以下情况:进程中有多个应用程序域,所有这些应用程序域均运行相同的代码。Use this setting when there are multiple application domains in the process, all of which run the same code.

  • LoaderOptimization.MultiDomainHost 以非特定于域的形式加载强名称程序集(如果它们以及它们的所有依赖项都已在全局程序集缓存中安装)。LoaderOptimization.MultiDomainHost loads strong-named assemblies as domain-neutral, if they and all their dependencies have been installed in the global assembly cache. 其他程序集都将针对它们加载的每个应用程序域分别进行加载和 JIT 编译,从而可以从进程中卸载。Other assemblies are loaded and JIT-compiled separately for each application domain in which they are loaded, and thus can be unloaded from the process. 如果您在同一进程中运行多个应用程序,或者如果您有混合的程序集,其中包括许多应用程序域共享的程序集和需要从进程中卸载的程序集,则可以使用此设置。Use this setting when running more than one application in the same process, or if you have a mixture of assemblies that are shared by many application domains and assemblies that need to be unloaded from the process.

以下程序集不能共享 JIT 编译代码:使用 LoadFrom 类的 Assembly 方法加载到“加载源”上下文中的程序集,或者使用 Load 方法的重载(指定字节数组)从图像加载的程序集。JIT-compiled code cannot be shared for assemblies loaded into the load-from context, using the LoadFrom method of the Assembly class, or loaded from images using overloads of the Load method that specify byte arrays.

使用 Ngen.exe(本机映像生成器)编译为本机代码的程序集如果在第一次加载到进程中时是以非特定于域的形式加载的,则可以在不同应用程序域之间共享这些程序集。Assemblies that have been compiled to native code by using the Ngen.exe (Native Image Generator) can be shared between application domains, if they are loaded domain-neutral the first time they are loaded into a process.

包含应用程序入口点的程序集的 JIT 编译代码只有在其所有依赖项都可以被共享的情况下,才可以被共享。JIT-compiled code for the assembly that contains the application entry point is shared only if all its dependencies can be shared.

非特定于域的程序集可以进行多次 JIT 编译。A domain-neutral assembly can be JIT-compiled more than once. 例如,如果两个应用程序域的安全授权集不同,则它们不能共享相同的 JIT 编译代码。For example, when the security grant sets of two application domains are different, they cannot share the same JIT-compiled code. 但是,JIT 编译程序集的每个副本都可以与其他具有相同授权集的应用程序域共享。However, each copy of the JIT-compiled assembly can be shared with other application domains that have the same grant set.

当您决定是否以非特定于域的形式加载程序集时,必须在减少内存占用和降低其他性能因素之间加以权衡。When you decide whether to load assemblies as domain-neutral, you must make a tradeoff between reducing memory use and other performance factors.

  • 对于非特定于域的程序集,对静态数据和方法的访问较慢的原因在于需要隔离程序集。Access to static data and methods is slower for domain-neutral assemblies because of the need to isolate assemblies. 访问该程序集的每一应用程序域都必须具有静态数据的单独副本,以避免跨域边界引用静态字段中的对象。Each application domain that accesses the assembly must have a separate copy of the static data, to prevent references to objects in static fields from crossing domain boundaries. 因此,运行时包含附加的逻辑,用以将调用方引导到静态数据或静态方法的适当副本。As a result, the runtime contains additional logic to direct a caller to the appropriate copy of the static data or method. 这一额外的逻辑将降低调用速度。This extra logic slows down the call.

  • 当以非特定于域的形式加载程序集时,必须找到并加载该程序集的所有依赖项,因为如果一个依赖项不能以非特定于域的形式加载,则会妨碍以非特定于域的形式加载程序集。All the dependencies of an assembly must be located and loaded when the assembly is loaded domain-neutral, because a dependency that cannot be loaded domain-neutral prevents the assembly from being loaded domain-neutral.

应用程序域和线程Application domains and threads

应用程序域为安全性、版本控制、可靠性和托管代码的卸载形成隔离边界。An application domain forms an isolation boundary for security, versioning, reliability, and unloading of managed code. 线程是公共语言运行时用来执行代码的操作系统构造。A thread is the operating system construct used by the common language runtime to execute code. 在运行时,所有托管代码均加载到一个应用程序域中,并由一个或多个托管线程运行。At run time, all managed code is loaded into an application domain and is run by one or more managed threads.

应用程序域和线程之间不具有一对一的相关性。There is not a one-to-one correlation between application domains and threads. 在任意给定时间,可以在单个应用程序域中执行几个线程,而且特定线程并不局限在单个应用程序域内。Several threads can execute in a single application domain at any given time, and a particular thread is not confined to a single application domain. 也就是说,线程可以自由跨越应用程序域边界;不为每个应用程序域创建新线程。That is, threads are free to cross application domain boundaries; a new thread is not created for each application domain.

在任意给定时间,每个线程都在一个应用程序域中执行。At any given time, every thread executes in an application domain. 在任何给定的应用程序域中,可能正在执行零个、一个或多个线程。Zero, one, or multiple threads might be executing in any given application domain. 运行时会跟踪在哪些应用程序域中有哪些线程正在运行。The runtime keeps track of which threads are running in which application domains. 通过调用 Thread.GetDomain 方法,您可以随时确定线程执行所在的域。You can locate the domain in which a thread is executing at any time by calling the Thread.GetDomain method.

应用程序域和区域性Application domains and cultures

区域性(由 CultureInfo 对象表示)与线程关联。Culture, which is represented by a CultureInfo object, is associated with threads. 您可以通过使用 CultureInfo.CurrentCulture 属性获取与当前正在执行的线程关联的区域性,并且您可以通过使用 Thread.CurrentCulture 属性获取或设置与当前正在执行的线程关联的区域性。You can get the culture that is associated with the currently executing thread by using the CultureInfo.CurrentCulture property, and you can get or set the culture that is associated with the currently executing thread by using the Thread.CurrentCulture property. 如果已使用 Thread.CurrentCulture 属性显式设置与线程关联的区域性,则当线程跨越应用程序域边界时,它将继续与该线程关联。If the culture that is associated with a thread has been explicitly set by using the Thread.CurrentCulture property, it continues to be associated with that thread when the thread crosses application domain boundaries. 否则,在任何给定时间内与线程关联的区域性将由线程执行所在的应用程序域中的 CultureInfo.DefaultThreadCurrentCulture 属性的值确定:Otherwise, the culture that is associated with the thread at any given time is determined by the value of the CultureInfo.DefaultThreadCurrentCulture property in the application domain in which the thread is executing:

  • 如果该属性的值不是 null,则由该属性返回的区域性与线程(并因此由 Thread.CurrentCultureCultureInfo.CurrentCulture 属性返回)关联。If the value of the property is not null, the culture that is returned by the property is associated with the thread (and therefore returned by the Thread.CurrentCulture and CultureInfo.CurrentCulture properties).

  • 如果该属性的值为 null,则当前系统区域性与线程关联。If the value of the property is null, the current system culture is associated with the thread.

对应用程序域进行编程Programming with application domains

应用程序域通常由运行时宿主以编程的方式来创建和操作。Application domains are usually created and manipulated programmatically by runtime hosts. 但是,有时应用程序还可能要和应用程序域结合起来使用。However, sometimes an application program might also want to work with application domains. 例如,应用程序可能将应用程序组件加载到域中以便能够在不停止整个应用程序的情况下卸载域(以及该组件)。For example, an application program could load an application component into a domain to be able to unload the domain (and the component) without having to stop the entire application.

AppDomain 是应用程序域的程序设计界面。The AppDomain is the programmatic interface to application domains. 此类包括各种方法,这些方法可以创建和卸载域、创建域中各类型的实例以及注册各种通知(如应用程序域卸载)。This class includes methods to create and unload domains, to create instances of types in domains, and to register for various notifications such as application domain unloading. 下表列出了常用的 AppDomain 方法。The following table lists commonly used AppDomain methods.

AppDomain 方法AppDomain Method 说明Description
CreateDomain 创建新的应用程序域。Creates a new application domain. 建议使用此方法指定 AppDomainSetup 对象的重载形式。It is recommended that you use an overload of this method that specifies an AppDomainSetup object. 这是设置新域的各个属性的首选方式,这些属性包括应用程序基(即该应用程序的根目录)、域的配置文件的位置、以及公共语言运行时用于将程序集加载到域中的搜索路径等。This is the preferred way to set the properties of a new domain, such as the application base, or root directory for the application; the location of the configuration file for the domain; and the search path that the common language runtime is to use to load assemblies into the domain.
ExecuteAssemblyExecuteAssemblyByNameExecuteAssembly and ExecuteAssemblyByName 执行应用程序域中的程序集。Executes an assembly in the application domain. 这是一个实例方法,因此它可用来执行另一个应用程序域(你拥有对该域的引用)中的代码。This is an instance method, so it can be used to execute code in another application domain to which you have a reference.
CreateInstanceAndUnwrap 在应用程序域中创建指定类型的实例,并返回一个代理。Creates an instance of a specified type in the application domain, and returns a proxy. 使用此方法以避免将包含创建的类型的程序集加载到调用程序集中。Use this method to avoid loading the assembly containing the created type into the calling assembly.
Unload 执行域的正常关闭。Performs a graceful shutdown of the domain. 只有应用程序域中正在运行的所有线程都已停止或域中不再有运行的线程之后,才卸载该应用程序域。The application domain is not unloaded until all threads running in the domain have either stopped or are no longer in the domain.

备注

公共语言运行时不支持全局方法序列化,因此不能使用委托来执行其他应用程序域中的全局方法。The common language runtime does not support serialization of global methods, so delegates cannot be used to execute global methods in other application domains.

公共语言运行时承载接口规范中介绍的非托管接口也提供对应用程序域的访问。The unmanaged interfaces described in the common language runtime Hosting Interfaces Specification also provide access to application domains. 运行时宿主可以使用非托管代码的接口在进程内创建应用程序域和获取对这些应用程序域的访问。Runtime hosts can use interfaces from unmanaged code to create and gain access to the application domains within a process.

COMPLUS_LoaderOptimization 环境变量The COMPLUS_LoaderOptimization environment variable

用于设置可执行应用程序的默认加载程序优化策略的环境变量。An environment variable that sets the default loader optimization policy of an executable application.

语法Syntax

COMPLUS_LoaderOptimization = 1  

备注Remarks

典型应用程序必须先将几个程序集加载到一个应用程序域中,然后才能执行其所包含的代码。A typical application loads several assemblies into an application domain before the code they contain can be executed.

程序集的加载方式决定了其实时 (JIT) 编译的代码是否可由进程中的多个应用程序域共享。The way the assembly is loaded determines whether its just-in-time (JIT) compiled code can be shared by multiple application domains in the process.

  • 如果程序集以非特定于域的形式加载,则所有共享相同安全授权集的应用程序域都可以共享相同的 JIT 编译代码。If an assembly is loaded domain-neutral, all application domains that share the same security grant set can share the same JIT-compiled code. 这将减少应用程序所需的内存。This reduces the memory required by the application.

  • 如果程序集不是以非特定于域的形式加载,则必须在加载程序集的每个应用程序域中对其进行 JIT 编译,并且加载程序不得跨应用程序域共享国际资源。If an assembly is not loaded domain-neutral, it must be JIT-compiled in every application domain in which it is loaded and the loader must not share internal resources across application domains.

在设置为 1 时,COMPLUS_LoaderOptimization 环境标志强制运行时主机以非特定于域的方式(称为 SingleDomain)加载所有程序集。When set to 1, the COMPLUS_LoaderOptimization environment flag forces the runtime host to load all assemblies in non-domain-neutral way known as SingleDomain. SingleDomain 不以非特定于域的形式加载任何程序集(Mscorlib 除外,它始终以非特定于域的形式加载)。SingleDomain loads no assemblies as domain-neutral, except Mscorlib, which is always loaded domain-neutral. 此设置称作单域,因为它通常用在宿主只运行进程中的单个应用程序时。This setting is called single domain because it is commonly used when the host is running only a single application in the process.

注意

COMPLUS_LoaderOptimization 环境标志旨在用于诊断和测试方案。The COMPLUS_LoaderOptimization environment flag was designed to be used in diagnostic and test scenarios. 启用该标志会导致速度严重减慢,并会增加内存使用率。Having the flag turned on can cause severe slow-down and increase in memory usage.

代码示例Code example

可通过将 COMPLUS_LoaderOptimization=1 追加到 HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\services\IISADMIN 键中环境的多字符串值中,来强制所有程序集不以 IISADMIN 服务的非特定于域的形式加载。To force all assemblies not to be loaded as domain-neutral for the IISADMIN service can be achieved by appending COMPLUS_LoaderOptimization=1 to the Environment’s Multi-String Value in the HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\services\IISADMIN key.

Key = HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\services\IISADMIN  
Name = Environment  
Type = REG_MULTI_SZ  
Value (to append) = COMPLUS_LoaderOptimization=1  

请参阅See also