从c#代码测量本机DLL内存使用情况

时间:2018-02-28 09:35:12

标签: c# .net memory measure

我正在使用供应商提供的C ++ DLL,我用DLLImport调用它来解析和处理包含许多对象类型的文件。

我需要在文件中的对象数量和内存使用量之间建立关联,以便(希望)能够防止有时发生的OutOfMemoryExceptions。

更新

要更清楚我要测量的内容和原因:预计会出现内存不足异常,因为一些非常复杂的文件需要加载7gb的内存(由perfmon测量):它们是3D地图有时是巨大而错综复杂的建筑物,从墙壁到单独的螺钉和螺栓,包括外面的树木和每个房间的桌椅。

由于DLL可以并行加载多个映射(它位于Web服务器上并且进程是共享的),因此加载2x 7gb文件可以理解地在具有8GB RAM的机器上触发OutOfMemoryException。 然而,7gb是非常罕见的,大多数地图占用大约500mb,有些需要1到2GB。

我们真正需要的是来查找内存泄漏(但是......),但能够在加载文件之前知道 它可能会有多少内存使用。因此,当用户尝试加载我们计算的文件时,可能需要大约2GB的RAM,而机器有1gb的空闲时间,我们会对此做些什么。从在Azure中启动新VM以防止用户工作,我们还不知道还有什么,但我们不能让DLL每次都崩溃整个服务器。

为了做到这一点,我想找出,例如,“DLL为每100个几何对象使用1mb内存”。

所以我有一堆要测试的文件(大约一百个),我想按顺序加载它们,测量本机DLL的内存使用情况(之前和之后),卸载文件,处理下一个文件。然后我得到一个包含所有数据的漂亮的CSV文件。

我已经尝试了System.Diagnostics.Process.GetCurrentProcess().VirtualMemorySize64,但它只给了我当前的进程内存,但DLL似乎不在当前进程中,因为大多数度量给出0个字节(文件加载前后的差异) )。
我也试过GC.GetTotalMemory(),但它并没有好多少,文件看起来都是1080字节。

private static void MeasureFilesMemoryUsage(string[] files) {
    foreach (var file in files) {
        var beforeLoad = MeasureMemoryUsage();

        wrapper.LoadFile(file)

        var afterLoad = MeasureMemoryUsage();

        wrapper.Unload();

        // save beforeLoad and afterLoad
    }
}

private static long MeasureMemoryUsage() {
    GC.Collect();
    GC.WaitForPendingFinalizers();
    GC.Collect();

    return System.Diagnostics.Process.GetCurrentProcess().VirtualMemorySize64;
}

我知道VMMAP或RedGate Ants Memory Profiler(或简称性能计数器)等工具,但这些不允许我将内存使用情况与特定的加载文件相匹配,我必须逐个加载文件,暂停程序,在工具中进行测量,并记下结果。不是我想对100个文件做的事情。

如何衡量.Net代码中特定C ++ DLL的内存使用情况?

1 个答案:

答案 0 :(得分:0)

在阅读了@HansPassant评论之后,我将测试分成两个程序:一个加载文件,另一个读取第一个的内存测量值。
在这里,他们被清理,以删除其他措施(如我的json文件中的项目数)和结果保存。

“措施”计划:

public static void Main(string[] args) {
    foreach (var document in Directory.EnumerateDirectories(JsonFolder)) {
        MeasureMemory(document);
    }
}

private static void MeasureMemory(string document) {
    // run process
    var proc = new Process {
        StartInfo = new ProcessStartInfo {
            FileName = "loader.exe",
            Arguments = document,
            WindowStyle = ProcessWindowStyle.Hidden,
            UseShellExecute = false,
            RedirectStandardOutput = true,
            CreateNoWindow = true
        }
    };
    proc.Start();

    // get process output
    var output = string.Empty;
    while (!proc.StandardOutput.EndOfStream) {
        output += proc.StandardOutput.ReadLine() + "\n";
    }

    proc.WaitForExit();

    // parse process output
    var processMemoryBeforeLoad = long.Parse(Regex.Match(output, "BEFORE ([\\d]+)", RegexOptions.Multiline).Groups[1].Value);
    var processMemoryAfterLoad = long.Parse(Regex.Match(output, "AFTER ([\\d]+)", RegexOptions.Multiline).Groups[1].Value);

    // save the measures in a CSV file
}

“装载机”程序:

public static int Main(string[] args) {
    var document = args[0];
    var files = Directory.EnumerateFiles(document);

    Console.WriteLine("BEFORE {0}", MeasureMemoryUsage());

    wrapper.LoadFiles(files);

    Console.WriteLine("AFTER {0}", MeasureMemoryUsage());

    wrapper.Unload();

    return 0;
}

private static long MeasureMemoryUsage() {
    // make sure GC has done its job
    GC.Collect();
    GC.WaitForPendingFinalizers();
    GC.Collect();

    return System.Diagnostics.Process.GetCurrentProcess().VirtualMemorySize64;
}