.NET 进程内存使用 = 5x CLR 堆内存?

Posted

技术标签:

【中文标题】.NET 进程内存使用 = 5x CLR 堆内存?【英文标题】:.NET process memory usage = 5x CLR Heap Memory? 【发布时间】:2012-04-24 16:51:57 【问题描述】:

我正在尝试解决一些内存使用问题。总的来说,我的应用程序收集了一些数据值并使用 C1 WPF 图表和数据网格将它们可视化,最终将所有内容放入 PDF 报告中。

使用 YourKit 分析我的进程我面临的情况是,CLR 堆大小约为 120MB(这很好),而进程内存大小约为 580MB。这几乎是我的实际 CLR 堆大小的内存消耗的 5 倍。我的 CLR 峰值大小为 220MB,而进程内存分配为 710MB。

我很清楚我的对象堆、堆栈等需要一些开销。在 Java JVM 中,我习惯的典型因素约为 1.5 倍。

如何解释这种过多的内存开销?这些进程是否只是分配空闲的备用堆空间?如果是,这是否解释了 710MB 与 220MB 的区别?

【问题讨论】:

【参考方案1】:

这里有几个附加说明。虽然我不完全确定“CLR 堆大小”是什么意思。 根据您使用的 .NET 运行时,CLR 使用 8 或 9 个不同的堆 - 因此您在堆大小与 VM 大小中看到的内存占差异的一些

    加载程序堆:包含 CLR 结构和类型系统 高频堆:静态、MethodTables、FieldDesc、接口映射 低频堆:EEClass、ClassLoader 和查找表 存根堆:用于 CAS、COM 包装器、P/Invoke 的存根 大型对象堆:需要超过 85k 字节的内存分配 GC 堆:用户分配的应用专用堆内存 JIT 代码堆:由 mscoreee(执行引擎)和托管代码的 JIT 编译器分配的内存 进程/基堆:互操作/非托管分配、本机内存等 在 .NET 5 中添加:Pinned Object Heap (POH)

另外两个可能导致过多内存使用的因素是内存碎片(主要发生在 LOH 或大型对象堆上)或大量线程。

内存碎片的原因有很多,排除这种情况的最佳方法是使用 WinDbg 分析 GC 堆上每个段的段大小。

就大量线程而言,您可以为应用使用的每个线程分配 1MB(对于 x86 进程)或 4MB(对于 x64 进程)的堆栈空间。此内存放置在进程/基堆中。因此,如果您有 100 个线程,则最多可以额外使用 100MB/400MB 的内存。

HTH

【讨论】:

非常感谢您的宝贵和广泛的笔记,尤其是。关于 .NET 内存分配的解剖结构和潜在问题。就我而言,它最终证明是通过 GDI+ 使用大块内存的 C1 图形组件。作为一名 Java 专家,我对无法使用 .NET 内存分析器工具看到这一点感到非常困惑。最后,我们必须通过限制这些 C1 图形组件的使用来解决/解决这个问题。 很高兴为您提供帮助。我不得不使用 WinDbg 调试许多内存转储,这教会了我很多东西。在使用 3rd 方组件时,我会注意两件事:首先,确保它们被处理掉,其次注意你的应用程序正在使用的句柄数量。这可以通过使用 TaskManager(确保 Handles col 可见)或 SysInternals ProcessExplorer 轻松完成。通常,您会使用“using()”语句来确保及时处理组件。但是我对 WPF 不熟悉,所以框架可能已经解决了这个问题。 Ben,你是如何发现 GDI+ 占用了内存的?【参考方案2】:

如果托管堆的总大小明显小于应用程序使用的私有字节,则很可能是您分配了非托管内存并且(可能)没有正确处理它。实现 IDisposable 的图形对象、流和其他对象需要在它们超出范围之前调用它们的 Dispose() 方法,或者将它们放在 using() 语句中,以便清理任何非托管资源。使用 ANTS Memory Profiler 之类的工具可以向您展示您的内存是如何分配的以及哪些对象实现了 IDisposable。

【讨论】:

谢谢丹!这正是我缺少的指针。可能 ANTS 在这里也可能无济于事,(我已经在使用 YourKit)因为它无法跟踪非托管内存泄漏。目前我正在尝试使用 DebugDiag 1.2 解决我的问题 我不熟悉 DebugDiag,但 Smartbear 的 AQTime 允许您分析托管和非托管代码。它不像 ANTS 那样容易使用,但有更多可用信息。见smartbear.com/products/qa-tools/…

以上是关于.NET 进程内存使用 = 5x CLR 堆内存?的主要内容,如果未能解决你的问题,请参考以下文章

.NET CLR 内存“所有堆中的字节数”远低于“Gen 0 堆大小”

获取另一个进程的 CLR 内存性能计数器的值

c# 垃圾回收。 .NET CLR 内存性能计数器显示 0 代表 gen 0 堆大小等。这是啥意思?

.Net 自动内存管理

C# Dispose Finalize

重温CLR(十五) 托管堆和垃圾回收