【问题标题】:Measure native DLL memory usage from c# code从 c# 代码测量本机 DLL 内存使用情况
【发布时间】:2018-02-28 09:35:12
【问题描述】:

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

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


更新

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

而且由于 DLL 可以并行加载多个映射(它在 Web 服务器上并且进程是共享的),加载 2x 7gb 文件可以理解地在具有 8gb 内存的机器上触发 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 的内存使用情况?

【问题讨论】:

  • DLL 不能分配进程外内存; DLL 中的代码在逻辑上是进程的一部分(因此,您无法区分每个 DLL 分配了多少内存,除非堆栈跟踪到分配函数)。 DLL 可以调用进程外 COM 服务器等,但这是不寻常的。如果进程分配内存,VirtualMemorySize64 应该会改变。也许包括一个简短的代码 sn-p 你正在测量的方式(和什么)。 (另外,PrivateMemorySize64 可能是更好的衡量标准。)
  • DllImport 将库加载到当前进程中一次 - 它不会卸载它(直到应用程序域死亡)。良好的 C++ 库在执行繁重的操作后不应该消耗额外的内存——所有资源都应该被它释放。它在运行过程中当然会消耗内存,但之后不会。所以不确定你到底想测量什么。
  • 1080 字节非常小。如果库完全分配了任何内容,则可能无法从已分配的页面中检测到它,因此逐个文件工作不太可能为您提供任何东西。如果您真的想检测内存不足的情况来自何处,那么您最好 1) 导致 OOM 和 2) 获取内存转储并分析它以查看它的来源。例如,它可能是一个非常小的内存泄漏,需要持续数小时才能看到任何东西,而分析单个负载不会显示这一点。
  • @JeroenMostert :我添加了我正在做的精简代码示例。 @Evk:DLL 加载要在 3D 中操作的文件,因此它将文件(或者更确切地说,文件的表示)保存在内存中,直到进程退出或我告诉它“销毁”文件。在示例中的LoadFile 之后,我们通常调用RotateZoom 等。
  • @JeroenMostert 1080 bytes 实际上是完全错误的。 Perfmon 告诉我,我加载的一些文件使用 2gb 内存,其中大多数使用大约 500mb。我有另一个示例程序,它只加载一个文件并且从不释放它,我可以用它来拍摄内存快照并手动进行测量,但我真的,真的不想为一百个文件做这件事。我不是试图追踪内存泄漏,而是正常的内存使用情况,以将其与文件数据相关联。我会尝试在我的帖子中更好地解释。

标签: c# .net memory measure


【解决方案1】:

阅读@HansPassant cmets 后,我将测试分为两个程序:一个用于加载文件,另一个用于读取第一个程序的内存测量值。
在这里,它们被清理以删除其他度量(例如我的 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;
}

【讨论】:

    猜你喜欢
    • 2015-09-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-01-31
    • 2014-06-03
    • 2018-03-22
    • 2011-02-14
    • 2017-02-14
    相关资源
    最近更新 更多