我正在研究的C#/ .NET应用程序正在遭受缓慢的内存泄漏.我已经使用CDB和SOS来尝试确定发生了什么,但数据似乎没有任何意义,所以我希望你们中的一个人之前可能已经经历过这种情况.
该应用程序在64位框架上运行.它不断地计算并将数据序列化到远程主机,并且正在大量地击中大对象堆(LOH).但是,我希望大多数LOH对象都是瞬态的:一旦计算完成并且已经发送到远程主机,就应该释放内存.然而,我所看到的是大量(实时)对象数组与空闲的内存块交织,例如,从LOH中获取一个随机段:
0:000> !DumpHeap 000000005b5b1000 000000006351da10
Address MT Size
...
000000005d4f92e0 0000064280c7c970 16147872
000000005e45f880 00000000001661d0 1901752 Free
000000005e62fd38 00000642788d8ba8 1056 <--
000000005e630158 00000000001661d0 5988848 Free
000000005ebe6348 00000642788d8ba8 1056
000000005ebe6768 00000000001661d0 6481336 Free
000000005f214d20 00000642788d8ba8 1056
000000005f215140 00000000001661d0 7346016 Free
000000005f9168a0 00000642788d8ba8 1056
000000005f916cc0 00000000001661d0 7611648 Free
00000000600591c0 00000642788d8ba8 1056
00000000600595e0 00000000001661d0 264808 Free
...
Run Code Online (Sandbox Code Playgroud)
显然,如果我的应用程序在每次计算期间创建长寿命的大对象,我会期望这种情况.(它确实这样做,我接受会有一定程度的LOH碎片,但这不是问题.)问题是你可以在上面的转储中看到的非常小的(1056字节)对象数组,我在代码中看不到正在创建,并以某种方式保持根深蒂固.
另请注意,转储堆段时CDB不报告类型:我不确定这是否相关.如果我转储标记的(< - )对象,CDB/SOS报告正常:
0:015> !DumpObj 000000005e62fd38
Name: System.Object[]
MethodTable: 00000642788d8ba8
EEClass: 00000642789d7660
Size: 1056(0x420) bytes
Array: Rank 1, Number of elements 128, Type CLASS …Run Code Online (Sandbox Code Playgroud) 在调试器中运行可视化C#项目时,由于2GB虚拟地址空间的碎片,我得到OutOfMemoryException,并且我们假设加载的dll可能是碎片的原因.
(有关更多信息,请参阅下面的问题开发历史)
嗨,我需要两个大的int数组加载到内存中,每个都有大约1.2亿个元素(~470MB),并且都在一个Visual C#项目中.
当我试图实例化第二个数组时,我得到一个OutOfMemoryException.
我确实有足够的总可用内存,在进行网络搜索之后,我认为我的问题是我的系统上没有足够大的连续空闲内存块.但! - 当我只在一个Visual C#实例中实例化一个数组,然后打开另一个Visual C#实例时,第二个实例可以实例化一个470MB的数组.(编辑以澄清:在上面的段落中,我的意思是在Visual C#的调试器中运行它)
任务管理器显示相应的内存使用量增加,就像您期望的那样.因此,整个系统上没有足够的连续内存块不是问题.然后我尝试运行一个编译的可执行文件,实例化两个数组也工作(内存使用1GB)
摘要:
Visual C#中的OutOfMemoryException使用两个大的int数组,但运行编译的exe工作(mem使用1GB)和两个单独的Visual C#实例能够为我的大数组找到两个足够大的连续内存块,但我需要一个Visual C#实例来能够提供记忆.
首先要特别感谢nobugz和Brian Rasmussen,我认为他们的预测是"过程中2GB虚拟地址空间的碎片化"是问题所在.
根据他们的建议,我使用VMMap和listdlls进行短暂的业余分析,得到:
*21个dll列为"独立"-exe.(工作和使用1GB内存的那个.)
*为vshost.exe版本列出的58个dll.(调试时运行的版本,抛出异常,仅使用500MB)
VMMap向我展示了调试器版本最大的可用内存块为262,175,167,155,108MB.
所以VMMap说没有连续的500MB块,根据有关空闲块的信息,我添加了~9个较小的int-arrays,这些内存使用量增加了超过1,2GB,实际上确实有效.
因此,我会说我们称之为"2GB虚拟地址空间碎片"有罪.
从listdll-output我创建了一个小的电子表格,其中十六进制数转换为十进制以检查dll之间的空闲区域,我确实找到了独立版本中间(21)dll的大空间,但不是vshost-debugger-version(58 dll文件).我并没有声称之间没有任何其他东西,我不确定我在那里做什么是有道理的,但它似乎与VMMaps分析一致,似乎单独的dll已经分裂了内存调试器版本.
因此,如果我能够减少调试器使用的dll数量,也许可以找到解决方案.
这可能吗?2.如果是,我该怎么做?
我有一个其他有效的问题这里关于这可能涉及LOH碎片可能还有其他未知因素中的一些绝望的内存问题.
我现在的问题是,接受的做事方式是什么?如果我的应用程序需要在Visual C#来完成,并且需要处理大型阵列的INT [4000000]调,我怎么能不被垃圾收集器拒绝处理LOH注定?
似乎我被迫将任何大型数组全局化,并且从不在它们周围使用"new"这个词.所以,我留下了带有"maxindex"变量的不合适的全局数组,而不是由函数传递的整齐大小的数组.
我总是被告知这是不好的做法.还有什么选择?
是否有某种功能System.GC.CollectLOH("Seriously")?是否有可能将垃圾收集外包给System.GC以外的其他方式?
无论如何,处理大型(> 85Kb)变量的普遍接受的规则是什么?