2

有时我们的客户会在我们的应用程序中观察到内存不足异常。由于我们记录了他们的操作,我们可以大致重现他们所做的事情,但是如果我这样做并使用 dotMemory 分析应用程序,我无法重现异常并且使用的内存(大约 100 MB 托管 + 500MB 非托管)远低于限制(2GB,因为它是一个 32 位应用程序)。此外,在捕获异常时,使用 Process.GetCurrentProcess().WorkingSet64 请求当前内存使用量,这表明内存使用量在 500 到 900 MB 之间。我知道这个数字不是很可靠,但这是另一个迹象,表明应该有足够的可用内存。

该应用程序的一个相关属性是它处理测量的时间序列(DateTime 对和存储在数组中的双精度数)。这些对象可能足够大,可以存储在大对象堆 (LOH) 中。因此,确实会发生堆碎片,但在分析时这似乎没什么大不了的LOH 的大小小于 100MB ,包括孔。

是否有可能在引发内存不足异常后调用垃圾收集器 (GC)?我认为,在内存分配请求不满足的情况下,只有当 GC 未能收集足够的内存时才会引发异常。但是,与在第 0 代堆中分配的内存相比,LOH 中分配的内存可能有所不同?

有谁知道,我们如何解决这个问题?

我们正在使用 VS 2010 SP1 和 .NET 4.0。该问题可能与此处此处此处提出的问题有关,但我没有在那里找到令人满意的答案。

更新:添加了示例性堆栈跟踪和堆碎片图表

没有触发内存不足异常的唯一地方,但由于它被请求,我添加了一个 strack 跟踪:

Exception of type 'System.OutOfMemoryException' was thrown.
mscorlib
  at System.Runtime.Serialization.ObjectIDGenerator.Rehash()
  at System.Runtime.Serialization.ObjectIDGenerator.GetId(Object obj, Boolean& firstTime)
  at System.Runtime.Serialization.Formatters.Binary.ObjectWriter.InternalGetId(Object obj, Boolean assignUniqueIdToValueType, Type type, Boolean& isNew)
  at System.Runtime.Serialization.Formatters.Binary.ObjectWriter.Schedule(Object obj, Boolean assignUniqueIdToValueType, Type type, WriteObjectInfo objectInfo)
  at System.Runtime.Serialization.Formatters.Binary.ObjectWriter.WriteMembers(NameInfo memberNameInfo, NameInfo memberTypeNameInfo, Object memberData, WriteObjectInfo objectInfo, NameInfo typeNameInfo, WriteObjectInfo memberObjectInfo)
  at System.Runtime.Serialization.Formatters.Binary.ObjectWriter.WriteMemberSetup(WriteObjectInfo objectInfo, NameInfo memberNameInfo, NameInfo typeNameInfo, String memberName, Type memberType, Object memberData, WriteObjectInfo memberObjectInfo)
  at System.Runtime.Serialization.Formatters.Binary.ObjectWriter.Write(WriteObjectInfo objectInfo, NameInfo memberNameInfo, NameInfo typeNameInfo, String[] memberNames, Type[] memberTypes, Object[] memberData, WriteObjectInfo[] memberObjectInfos)
  at System.Runtime.Serialization.Formatters.Binary.ObjectWriter.Write(WriteObjectInfo objectInfo, NameInfo memberNameInfo, NameInfo typeNameInfo)
  at System.Runtime.Serialization.Formatters.Binary.ObjectWriter.Serialize(Object graph, Header[] inHeaders, __BinaryWriter serWriter, Boolean fCheck)
  at System.Runtime.Serialization.Formatters.Binary.BinaryFormatter.Serialize(Stream serializationStream, Object graph, Header[] headers, Boolean fCheck)
  ... <methods from our application follow>

dotMemory 的下图描述了使用该工具大约一个小时后的 LOH 碎片:

在此处输入图像描述

4

1 回答 1

2

使用工具vmmap我找到了问题的原因:托管堆可用的实际内存远小于 2GB 的限制。这是为与 MS Office 工具交互而加载的几个共享库 (~400 MB)。还有一些本地代码 dll (~300MB) 也分配非托管堆 (~300MB)。还有很多其他的东西,最后,托管堆只剩下大约 700MB。

由于可用内存比我最初想象的要少得多,因此 LOH 碎片的影响可能比我想象的要大,实际上:vmmap 显示,即使可用内存保持不变,该内存区域中的最大空闲块也会随着时间的推移而变小. 我认为,这证明了碎片化是问题的原因。异常的触发通常是我们有时用于深度复制对象的二进制序列化。它似乎会导致内存使用量达到峰值。

那么该怎么办呢?我正在考虑以下选项:

  • 切换到 x64(从长远来看,这将发生)
  • 切换到允许对LOH进行碎片整理的 .NET 4.5.1
  • 减少一般内存使用:如果另外有大约 200MB 可用,碎片整理似乎需要更长的时间。当某些大型库未加载时会发生这种情况。在我的实验中,我无法再触发内存不足异常。
  • 更改代码,这可能会太耗时
于 2015-12-01T16:02:55.423 回答