use*_*727 16 c# memory visual-studio-2008
在调试器中运行可视化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.如果是,我该怎么做?
您正在与虚拟内存地址空间碎片作斗争.32位版本的Windows上的进程有2千兆字节的可用内存.该内存由代码和数据共享.代码块是CLR和JIT编译器以及ngen-ed框架程序集.数据块是.NET使用的各种堆,包括加载器堆(静态变量)和垃圾收集堆.这些块位于存储器映射中的各种地址处.可用内存可供您分配阵列.
问题是,大型阵列需要连续的内存块.地址空间中的代码和数据块之间的"漏洞"不够大,无法分配如此大的数组.第一个洞通常在450到550兆字节之间,这就是你的第一个阵列分配成功的原因.下一个可用的洞要小得多.太小了,以适应另一个大阵列,即使你有一个容易的千兆字节的可用内存,你也会得到OOM.
您可以使用SysInternals的VMMap实用程序查看进程的虚拟内存布局.好的诊断,但它不会解决您的问题.只有一个真正的修复,转移到64位版本的Windows.也许更好:重新考虑您的算法,因此它不需要这么大的数组.
第3次更新:通过禁用Visual Studio托管过程(项目属性,调试),可以显着减少加载的DLL数量.这样做仍然允许您调试应用程序,但它将摆脱许多 DLL和一些帮助程序线程.
在一个小的测试项目中,当我禁用托管进程时,加载的DLL的数量从69个增加到34个.我也摆脱了10多个线程.总而言之,内存使用量的显着减少也应该有助于减少堆碎片.
有关托管过程的其他信息:http://msdn.microsoft.com/en-us/library/ms242202.aspx
您可以在新应用程序中加载第二个阵列的原因是每个进程都获得一个完整的2 GB虚拟地址空间.即操作系统将交换页面以允许每个进程处理总内存量.当您尝试在一个进程中分配两个数组时,运行时必须能够分配两个所需大小的连续块.你在数组中存储什么?如果存储对象,则每个对象都需要额外的空间.
请记住,应用程序实际上并不请求物理内存.相反,每个应用程序都有一个地址空间,可以从中分配虚拟内存.然后,OS将虚拟内存映射到物理内存.这是一个相当复杂的过程(Russinovich花了100多页关于Windows如何在他的Windows内部书中处理内存).有关Windows如何执行此操作的详细信息,请参阅http://blogs.technet.com/markrussinovich/archive/2008/11/17/3155406.aspx
更新:我一直在思考这个问题,听起来有点奇怪.通过Visual Studio运行应用程序时,您可能会看到根据您的配置加载的其他模块.在我的设置中,由于分析器和TypeMock(通过探查器钩子实际上它的魔力),我在调试期间加载了许多不同的DLL.
根据这些的大小和加载地址,它们可能会阻止运行时分配连续的内存.话虽如此,我仍然有点惊讶的是,在分配了两个大型阵列后,你得到一个OOM,因为它们的组合大小小于1 GB.
您可以使用listdllsSysInternals中的工具查看加载的DLL .它会显示加载地址和大小.或者,您可以使用WinDbg.该lm命令显示已加载的模块.如果您还需要大小,则需要指定v详细输出的选项.WinDbg还允许您检查.NET堆,这可以帮助您找出无法分配内存的原因.
第二次更新:如果您使用的是Windows XP,则可以尝试重新绑定一些加载的DLL以释放更多连续的空间.Vista和Windows 7使用ASLR,因此我不确定您是否会受益于这些平台上的变基.