在C#中获取总分配总数

问题描述 投票:-1回答:2

是否有一种方法可以获取分配的总数(注意-分配数量,而不是分配的字节数?它可以是当前线程,也可以是全局线程,以较容易的一个为准。

[我想检查一个特定函数分配了多少个对象,虽然我知道调试-> Performance Profiler(Alt + F2),但我希望能够从程序内部以编程方式进行操作。

// pseudocode
int GetTotalAllocations() {
    ...;
}    
class Foo {
    string bar;
    string baz;
}
public static void Main() {
    int allocationsBefore = GetTotalAllocations();
    PauseGarbageCollector(); // do I need this? I don't want the GC to run during the function and skew the number of allocations
    // Some code that makes allocations.
    var foo = new Foo() { bar = "bar", baz = "baz" };
    ResumeGarbageCollector();
    int allocationsAfter = GetTotalAllocations();
    Console.WriteLine(allocationsAfter - allocationsBefore); // Should print 3 allocations - one for Foo, and 2 for its fields.
}

此外,我是否需要暂停垃圾收集以获取准确的数据,我可以这样做吗?

我需要使用CLR分析API来实现吗?

c# allocation .net-core-3.1
2个回答
0
投票

[您需要使用一些kernel32函数,但是有可能!! :)我没有编写完整的代码,但是希望您能感觉应该怎么做。

首先,您需要使用功能进行所有处理:Process.GetProcesseslink那么您需要从中创建快照CreateToolhelp32Snapshot,因为此快照不需要“ GC暂停”,并且需要创建循环以枚举所有内存块之后。使用Heap32ListFirstHeap32First初始化循环功能,然后可以调用Heap32Next直到成功。

并且当它在您的代码中这样声明时,您可以调用kerner32函数:

[DllImport("kernel32", SetLastError = true, CharSet = System.Runtime.InteropServices.CharSet.Auto)]
static extern IntPtr CreateToolhelp32Snapshot([In]UInt32 dwFlags, [In]UInt32 th32ProcessID);

这里是c ++示例,但是您可以在CSharp函数声明之后执行相同的操作:Traversing the Heap List

我知道这并不容易,但是没有简单的方法。顺便说一下,如果您在循环内调用Toolhelp32ReadProcessMemory,则可以检索许多有用的其他信息。


而且我发现pinvoke.net可能对您有帮助pinvoke.net

https://www.pinvoke.net/default.aspx/kernel32.createtoolhelp32snapshothttps://www.pinvoke.net/default.aspx/kernel32.Heap32ListFirst


0
投票

至少在当前情况下,您要问的是不可能的,让我们看看为什么。

有没有办法获得分配总数

是的,有可能。但是我假设您认为每次使用new运算符创建的对象都会在堆上发生分配。那不是真的堆是按段分配的。在填满最后一个分配的段之前,GC可能会分配几个new操作员调用,然后GC决定分配一个新的。enter image description here

此图说明了这种情况:堆中有几个段,但是它们被不同的对象填充。

enter image description here

因此有一种方法可以获取分配的段数,但是它们不对应于对象数,它们的大小也可以不同;第一个段可以为100个字节,另一个段可以为800个字节。

我要检查特定功能分配了多少个对象

您可以在给定的时刻获得多少个对象。无法将信息缩小到特定功能。您可以假设,如果您两次计算堆中有多少个对象,一次是在函数调用之前,一次是在函数调用之后,那么您将能够从另一个中减去一个值并获得差值。

这里的问题是,可以在应用程序中的这两次测量之间分配数百万个对象,因此您的函数分配将显得微不足道,无法获得真实的画面并将函数分配与所有其他对象区分开。

我编写了代码来说明这些方法,您可以找到它here。我使用EnumerateObjectAddresses获取堆上对象的地址,并使用ETW的TraceEvenSession.Source.Clr.GCAllocationTick获取有关段分配的事件。

© www.soinside.com 2019 - 2024. All rights reserved.