【发布时间】:2010-10-09 22:12:21
【问题描述】:
我最近开始使用 ANTS 分析工具进行生产工作。除了对它们的出色表现感到惊讶之外,我不禁想知道它们是如何工作的。例如,最有用的功能之一是让您可视化正在运行的程序的全局根,以及对不同类型值的引用数量。
此工具如何获取这些信息?
【问题讨论】:
我最近开始使用 ANTS 分析工具进行生产工作。除了对它们的出色表现感到惊讶之外,我不禁想知道它们是如何工作的。例如,最有用的功能之一是让您可视化正在运行的程序的全局根,以及对不同类型值的引用数量。
此工具如何获取这些信息?
【问题讨论】:
(完全披露:我在 Visual Studio Profiler 团队,但以下信息是公开的)
您可以通过编写一个在您的目标进程内运行的 CLR 分析器来做到这一点。 CLR 分析器是 C++ COM 对象,当设置 COR_PROFILER 和 COR_PROFILING_ENABLED 环境变量时,运行时会实例化它们(请参阅 here)。有两个主要的CLR profiling interfaces,具体来说,ICorProfilerCallback 和ICorProfilerInfo。 ICorProfilerCallback 是 CLR 用来通知您订阅的特定事件(模块加载、函数 JIT 编译、线程创建、GC 事件),而您的分析器可以使用 ICorProfilerInfo 来获取有关线程的其他信息,加载程序集的模块、类型、方法和元数据。您可以使用此接口获取有关分配类型的符号信息。
使用您的分析器进行中,您可以通过ICorProfilerInfo::ForceGC 强制进行 GC。 GC 完成后,您的分析器将通过ICorProfilerCallback2::GarbageCollectionFinished 收到通知,您将通过ICorProfilerCallback2::RootReferences2 获得根引用。当您将根引用信息与ICorProfilerCallback::ObjectReferences 结合时,您可以获得.NET 应用程序的完整对象引用图。
您可以通过使用ICorProfilerCallback::ObjectAllocated 回调来确定何时创建各个CLR 对象来获取更多实时信息。但是,这可能会很昂贵,因为您至少会为每个分配的对象产生一个额外的函数调用。您可以通过将 CLR 分配的 ObjectID 映射到您自己的内部 ID 来跟踪单个对象。给定对象的ObjectID 是一个临时指针,因为它可以随着垃圾回收的发生而改变,这可能导致对象在压缩期间移动。这个过程在here 中说明。您可以使用来自ICorProfilerCallback::MovedReferences 的信息来跟踪移动的物体。
为了激活上述回调,您需要告诉 CLR 分析 API 您对它们感兴趣。您可以通过在调用 ICorProfilingInfo::SetEventMask 时指定 COR_PRF_MONITOR_GC 和 COR_PRF_MONITOR_OBJECT_ALLOCATED 作为事件标志的一部分来做到这一点。
David Broman 是 CLR 分析器的开发人员,his blog 拥有大量关于一般分析的重要信息,包括您可能遇到的所有疯狂陷阱和问题。
【讨论】:
像 ANTS 这样的分析器使用 CLR 本身提供的“分析 API”,它可以很简单地告诉您 CLR 内部发生了什么。例如,在分配对象时会发生一个 API 回调方法,恰当地命名为ObjectAllocated()。同样,当输入方法、创建线程等时也会发生事件。
原始分析 API 称为 ICorProfilerCallback。更高版本称为 CoreProfilerCallback2 和 CoreProfilerCallback3。如果你用谷歌搜索这些名字,你会找到你正在寻找的答案。在 codeproject 你可以看到一个实际的例子:Creating a Custom .NET Profiler
最后一点:不能在 C# 和 VB.NET 等托管代码中使用 API。它仅适用于非托管代码,例如C 或 C++。因此,例如,C# 应用程序不能使用此 API 来检查自己的行为和对象。
【讨论】: