应用程序域资源监控Application Domain Resource Monitoring

借助应用域资源监视 (ARM),主机可以通过应用域监视 CPU 和内存使用情况。Application domain resource monitoring (ARM) enables hosts to monitor CPU and memory usage by application domain. 对于在长时间运行的进程中使用多个应用域的 ASP.NET 等主机,这就很有用。This is useful for hosts such as ASP.NET that use many application domains in a long-running process. 主机可以卸载对整个进程的性能有不利影响的应用的应用域,但仅当能够发现有问题的应用时,才可以这样做。The host can unload the application domain of an application that is adversely affecting the performance of the entire process, but only if it can identify the problematic application. ARM 提供的信息就有助于作出此类决定。ARM provides information that can be used to assist in making such decisions.

例如,托管服务可能有多个应用在 ASP.NET 服务器上运行。For example, a hosting service might have many applications running on an ASP.NET server. 如果进程中的一个应用开始占用太多内存或过长的处理器时间,托管服务就可以使用 ARM 发现导致问题发生的应用域。If one application in the process begins consuming too much memory or too much processor time, the hosting service can use ARM to identify the application domain that is causing the problem.

ARM 是轻型服务,足可用于实际应用。ARM is sufficiently lightweight to use in live applications. 若要访问信息,可以使用 Windows 事件跟踪 (ETW),或直接使用托管或本机 API。You can access the information by using event tracing for Windows (ETW) or directly through managed or native APIs.

启用资源监视Enabling Resource Monitoring

可以通过下列四种方法启用 ARM:在公共语言运行时 (CLR) 启动时提供配置文件、使用非托管宿主 API、使用托管代码或侦听 ARM ETW 事件。ARM can be enabled in four ways: by supplying a configuration file when the common language runtime (CLR) is started, by using an unmanaged hosting API, by using managed code, or by listening to ARM ETW events.

一旦启用,ARM 就会开始收集进程中所有应用域的相关数据。如果在启用 ARM 前就创建了应用域,累积数据会在 ARM 启用时启动,而不是在应用域创建时启动。一旦启用,ARM 便无法再禁用。As soon as ARM is enabled, it begins collecting data on all application domains in the process.If an application domain was created before ARM is enabled, cumulative data starts when ARM is enabled, not when the application domain was created.Once it is enabled, ARM cannot be disabled.

  • 可以在 CLR 启动时启用 ARM,具体操作是向配置文件添加 <appDomainResourceMonitoring> 元素,并将 enabled 属性设置为 trueYou can enable ARM at CLR startup by adding the <appDomainResourceMonitoring> element to the configuration file, and setting the enabled attribute to true. false(默认值)只表示不在启动时启用 ARM;稍后可以使用其他激活机制之一来激活它。A value of false (the default) means only that ARM is not enabled at startup; you can activate it later by using one of the other activation mechanisms.

  • 主机可以请求获取 ICLRAppDomainResourceMonitor 托管接口来启用 ARM。The host can enable ARM by requesting the ICLRAppDomainResourceMonitor hosting interface. 成功获取此接口后,就会启用 ARM。Once this interface is successfully obtained, ARM is enabled.

  • 托管代码可以将静态 AppDomain.MonitoringIsEnabled 属性(Visual Basic 中的 Shared)设置为 true,从而启用 ARM。Managed code can enable ARM by setting the static (Shared in Visual Basic) AppDomain.MonitoringIsEnabled property to true. 设置此属性后,就会启用 ARM。As soon as the property is set, ARM is enabled.

  • 启动后,可以通过侦听 ETW 事件来启用 ARM。You can enable ARM after startup by listening to ETW events. 使用 AppDomainResourceManagementKeyword 关键字启用公共提供程序 Microsoft-Windows-DotNETRuntime 后,ARM 便会启用,并开始抛出所有应用域的事件。ARM is enabled and begins raising events for all application domains when you enable the public provider Microsoft-Windows-DotNETRuntime by using the AppDomainResourceManagementKeyword keyword. 若要将数据与应用域及线程相关联,还必须使用 ThreadingKeyword 关键字启用 Microsoft-Windows-DotNETRuntimeRundown 提供程序。To correlate data with application domains and threads, you must also enable the Microsoft-Windows-DotNETRuntimeRundown provider with the ThreadingKeyword keyword.

使用 ARMUsing ARM

ARM 提供应用域使用的总处理器时间,以及关于内存使用情况的三种信息。ARM provides the total processor time that is used by an application domain and three kinds of information about memory use.

  • 应用域使用的总处理器时间(以秒为单位):此时间的计算方式为,将操作系统报告的线程时间相加得出,包括在生存期内在应用域中执行的所有线程。Total processor time for an application domain, in seconds: This is calculated by adding up the thread times reported by the operating system for all threads that spent time executing in the application domain during its lifetime. 受阻止或处于睡眠状态的线程不使用处理器时间。Blocked or sleeping threads do not use processor time. 如果线程调用本机代码,线程在本机代码中花费的时间计入执行调用的应用域的总处理器时间。When a thread calls into native code, the time that the thread spends in native code is included in the count for the application domain where the call was made.

  • 应用程序域在其生命周期内进行的托管分配总量(以字节为单位):总分配并不总是反映应用程序域的内存使用情况,因为所分配的对象可能是短期的。Total managed allocations made by an application domain during its lifetime, in bytes: Total allocations do not always reflect memory use by an application domain, because the allocated objects might be short-lived. 不过,如果应用分配并释放大量对象,分配成本可能会非常高。However, if an application allocates and frees huge numbers of objects, the cost of the allocations could be significant.

  • 应用域引用且在最新执行的完全阻止式回收后保留的托管内存(以字节为单位):此数字只有在执行完全阻止式回收后才准确。Managed memory, in bytes, that is referenced by an application domain and that survived the most recent full, blocking collection: This number is accurate only after a full, blocking collection. (这与并发回收相反,后者发生在后台,不会阻止应用。)例如,GC.Collect() 方法重载导致执行完全阻止式回收。(This is in contrast to concurrent collections, which occur in the background and do not block the application.) For example, the GC.Collect() method overload causes a full, blocking collection.

  • 进程引用且在最新执行的完全阻止式回收后保留的托管内存总量(以字节为单位):可将为单个应用程序域保留的内存与此数字进行比较。Total managed memory, in bytes, that is referenced by the process and that survived the most recent full, blocking collection: The survived memory for individual application domains can be compared to this number.

确定何时发生完全阻止式回收Determining When a Full, Blocking Collection Occurs

若要确定何时保留的内存计数是准确的,只需知道何时发生了完全阻止式回收即可。To determine when counts of survived memory are accurate, you need to know when a full, blocking collection has just occurred. 执行此操作的方法取决于用来检查 ARM 统计信息的 API。The method for doing this depends on the API you use to examine ARM statistics.

托管 APIManaged API

如果使用 AppDomain 类的属性,可以使用 GC.RegisterForFullGCNotification 方法来注册获取完全回收的通知。If you use the properties of the AppDomain class, you can use the GC.RegisterForFullGCNotification method to register for notification of full collections. 使用的阈值并不重要,因为正在等待回收完成,而不是回收的方法完成。The threshold you use is not important, because you are waiting for the completion of a collection rather than the approach of a collection. 然后,可以调用 GC.WaitForFullGCComplete 方法,一直阻止到完全回收完成。You can then call the GC.WaitForFullGCComplete method, which blocks until a full collection has completed. 可以创建线程,用于在循环中调用此方法,并在此方法返回结果时执行任何所需的分析。You can create a thread that calls the method in a loop and does any necessary analysis whenever the method returns.

也可以定期调用 GC.CollectionCount 方法,以确定第 2 代回收计数是否已增加。Alternatively, you can call the GC.CollectionCount method periodically to see if the count of generation 2 collections has increased. 此方法可能无法准确指明完全回收的发生,具体视轮询频率而定。Depending on the polling frequency, this technique might not provide as accurate an indication of the occurrence of a full collection.

宿主 APIHosting API

如果使用非托管宿主 API,主机必须向 CLR 传递 IHostGCManager 接口实现。If you use the unmanaged hosting API, your host must pass the CLR an implementation of the IHostGCManager interface. 如果 CLR 恢复执行在回收发生时被暂停的线程,便会调用 IHostGCManager::SuspensionEnding 方法。The CLR calls the IHostGCManager::SuspensionEnding method when it resumes execution of threads that have been suspended while a collection occurs. CLR 将生成的已完成回收作为方法参数进行传递,以便主机能够确定回收是完全回收还是部分回收。The CLR passes the generation of the completed collection as a parameter of the method, so the host can determine whether the collection was full or partial. 实现 IHostGCManager::SuspensionEnding 方法可以查询保留的内存,以确保在内存更新时立即检索计数。Your implementation of the IHostGCManager::SuspensionEnding method can query for survived memory, to ensure that the counts are retrieved as soon as they are updated.

请参阅See also