应用程序域资源监视(ARM)使主机能够监视应用程序域的 CPU 和内存使用情况。 这对于在长时间运行过程中使用许多应用程序域的主机(例如 ASP.NET)非常有用。 主机可以卸载对整个进程性能产生不利影响的应用程序的应用程序域,但前提是它可以识别有问题的应用程序。 ARM 提供了可用于帮助做出此类决策的信息。
例如,托管服务可能有许多应用程序在 ASP.NET 服务器上运行。 如果进程中的一个应用程序开始消耗过多的内存或过多的处理器时间,则托管服务可以使用 ARM 来确定导致问题的应用程序域。
ARM 足够轻量级,可用于实时应用程序。 可以使用 Windows 事件跟踪(ETW)或通过托管或本机 API 直接访问信息。
启用资源监视
可以通过四种方式启用 ARM:通过在公共语言运行时(CLR)启动时提供配置文件、使用非托管托管 API、使用托管代码或侦听 ARM ETW 事件来提供配置文件。
一旦启用 ARM,它将开始收集有关进程中所有应用程序域的数据。如果在启用 ARM 之前创建了应用程序域,则会在启用 ARM 时启动累积数据,而不是在创建应用程序域时启动。启用后,无法禁用 ARM。
可以通过将<appDomainResourceMonitoring>元素添加到配置文件中,并将
enabled
属性设置为true
,在CLR启动时启用ARM。 值false
(默认值)仅表示在启动时未启用 ARM;稍后可以使用其他激活机制之一来激活它。主机可以通过请求 ICLRAppDomainResourceMonitor 托管接口来启用 ARM。 成功获取此接口后,将启用 ARM。
托管代码可以通过将静态(
Shared
在 Visual Basic 中)AppDomain.MonitoringIsEnabled 属性设置为true
来启用 ARM。 设置属性后,将立即启用 ARM。可以通过侦听 ETW 事件在启动后启用 ARM。 启用公共提供程序
Microsoft-Windows-DotNETRuntime
并使用AppDomainResourceManagementKeyword
关键字时,ARM 将被启用,并开始为所有应用程序域引发事件。 若要将数据与应用程序域和线程相关联,还必须启用Microsoft-Windows-DotNETRuntimeRundown
提供程序并使用ThreadingKeyword
关键字。
使用 ARM
ARM 提供应用程序域使用的总处理器时间以及有关内存使用的三种信息。
应用程序域的总处理器时间(以秒为单位):通过将由操作系统报告的所有线程在其生命周期内在应用程序域中执行的线程时间加起来计算。 被阻塞或睡眠的线程不使用CPU时间。 当线程调用本机代码时,线程在本机代码中花费的时间包含在进行调用的应用程序域的计数中。
托管 API:AppDomain.MonitoringTotalProcessorTime 属性。
托管 API: ICLRAppDomainResourceMonitor::GetCurrentCpuTime 方法。
ETW 事件:
ThreadCreated
事件、ThreadAppDomainEnter
事件和ThreadTerminated
事件。 若要了解提供程序和关键字,请参阅 CLR ETW 事件 中的“应用域资源监视事件”。
应用程序域在其生存期内进行的托管分配总数(以字节为单位):总分配并不总是反映应用程序域的内存使用,因为分配的对象可能生存期较短。 但是,如果应用程序分配并释放了大量对象,则分配的成本可能很大。
托管 API:AppDomain.MonitoringTotalAllocatedMemorySize 属性。
宿主 API:ICLRAppDomainResourceMonitor::GetCurrentAllocated 方法。
ETW 事件:
AppDomainMemAllocated
事件、Allocated
字段。
应用域引用且在最新执行的完全阻止式回收后保留的托管内存(以字节为单位) :此数字只有在执行完全阻止式回收后才准确。 (这与并发回收相反,后者发生在后台,不会阻止应用。)例如,GC.Collect() 方法重载导致执行完全阻止式回收。
托管 API:AppDomain.MonitoringSurvivedMemorySize 属性。
宿主 API:ICLRAppDomainResourceMonitor::GetCurrentSurvived 方法、
pAppDomainBytesSurvived
参数。ETW 事件:
AppDomainMemSurvived
事件、Survived
字段。
进程引用且在最新执行的完全阻止式回收后保留的托管内存总量(以字节为单位) :可将为单个应用程序域保留的内存与此数字进行比较。
宿主 API:ICLRAppDomainResourceMonitor::GetCurrentSurvived 方法、
pTotalBytesSurvived
参数。ETW 事件:
AppDomainMemSurvived
事件、ProcessSurvived
字段。
确定何时发生完全阻止式回收
若要确定何时保留的内存计数是准确的,只需知道何时发生了完全阻止式回收即可。 执行此作的方法取决于用于检查 ARM 统计信息的 API。
托管 API
如果使用 AppDomain 类的属性,可以使用 GC.RegisterForFullGCNotification 方法来注册获取完全回收的通知。 使用的阈值并不重要,因为正在等待回收完成,而不是回收的方法完成。 然后,可以调用方法 GC.WaitForFullGCComplete ,该方法会阻止直到完整集合完成。 可以创建一个线程,该线程在循环中调用该方法,并在该方法返回时执行任何必要的分析。
或者,可以定期调用 GC.CollectionCount 该方法,以查看第 2 代集合的计数是否已增加。 根据轮询频率,此方法可能无法准确指示完整集合的发生情况。
托管 API
如果使用非托管宿主 API,主机必须向 CLR 传递 IHostGCManager 接口实现。 如果 CLR 恢复执行在回收发生时被暂停的线程,便会调用 IHostGCManager::SuspensionEnding 方法。 CLR 将已完成集合的生成作为方法的参数传递,因此主机可以确定集合是完整集合还是部分集合。 实现 IHostGCManager::SuspensionEnding 方法可以查询保留的内存,以确保在内存更新时立即检索计数。