8

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

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

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

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

4

2 回答 2

14

这里有几个额外的说明。尽管我不确定您所说的“CLR 堆大小”是什么意思。根据您使用的 .NET 运行时,CLR 使用 8 或 9 个不同的堆 - 因此您在堆大小与 VM 大小中看到的内存说明了一些差异:

  1. Loader Heap:包含 CLR 结构和类型系统
  2. 高频堆:statics, MethodTables, FieldDescs, interface map
  3. 低频堆:EEClass、ClassLoader 和查找表
  4. 存根堆:用于 CAS、COM 包装器、P/Invoke 的存根
  5. 大对象堆:需要超过 85k 字节的内存分配
  6. GC Heap:用户分配给应用程序私有的堆内存
  7. JIT 代码堆:由 mscoreee(执行引擎)和用于托管代码的 JIT 编译器分配的内存
  8. 进程/基堆:互操作/非托管分配、本机内存等
  9. 在 .NET 5 中添加固定对象堆 (POH)

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

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

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

高温高压

于 2012-08-22T14:54:25.970 回答
2

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

于 2012-04-12T12:58:31.363 回答