类型为'System.OutOfMemoryException'的异常被抛出。

3
我突然在两个不同的机器上运行两个程序时遇到了内存异常错误,尽管似乎有足够的内存,但仍然出现该问题。我在程序中创建了多个线程,所以不确定这是否适用于此论坛,但它是否与Visual Studio相关或者绝对是内存问题。其中一个程序在我的台式电脑上使用Visual Studio 2008和2GB RAM运行。另一个程序在使用Visual Basic 2008 Express和4GB RAM的Windows 2003服务器上运行。 现在,该模块需要读取一个大型XML文件并将其拆分并存储在字符串数组中。现在,块的数量最多可以达到10000个。我知道这很大,但我已经运行了一个多月了,从未遇到过这个问题。我注意到的唯一可能相关的问题是我在硬盘上的空间不足,但这很快就被清理解决了。哦,是的,我的机器处理器是2.13 GHZ的双核心。 这是一个控制台程序,可以进行多个Web请求,但是如上所述,内存问题只出现在一个特定的模块中。
Public Shared Function textLoad(ByVal _html As String) As Boolean
    Try 
            //_html is the filestream that was read in

        Dim defaultHeading = "xmlns:gnip=""http://www.gnip.com/schemas/2010"" xmlns=""http://www.w3.org/2005/Atom"""
        Dim header_of_xml As String = "<?xml version=""1.0"" encoding=""utf-8""?>" & vbNewLine & "<entry " & defaultHeading & ">"
        Dim footer_of_xml As String = "</entry>"
        Dim entry As String = String.Empty
        Dim xrs As XmlReaderSettings = New XmlReaderSettings()
        Dim dupeArray As New ArrayList
        Dim stringSplitter() As String = {"</entry>"}

        //split the file content based on the closing entry tag
        sampleResults = Nothing
        sampleResults = _html.Split(stringSplitter, StringSplitOptions.RemoveEmptyEntries)
        entryResults.Clear()

        If getEntryId(sampleResults) Then
           // the following loops seem clumsy but I wanted to dedupe the lists to //ensure that I am not adding duplicate entries and I do this by going to the getEntryID //method and extracting the ids and then comparing them below 

            For i As Integer = 0 To sampleResults.Count - 1
                For j As Integer = 0 To idList.Count - 1
                    If sampleResults(i).Contains(idList.Item(j)) AndAlso Not dupeArray.Contains(idList.Item(j)) Then
                        dupeArray.Add(idList.Item(j))
                        entry = sampleResults(i)

我查看了任务管理器,以识别该程序使用的资源,以下是情况:

Parser.exe CPU = 34 内存使用 = 349,500 K

没有其他密集运行的任务

编辑 _-

确定问题的确切来源:

**sampleResults = _html.Split(stringSplitter, StringSplitOptions.RemoveEmptyEntries)**

有人能注意到这里有什么问题吗?


这可能应该放在stackoverflow.com上,但我没有足够的声望来投票关闭。 - Tomas Aschan
@TomasLycken 你可以随时标记它 -> 标记 -> 不属于这里 -> 属于 X。 - Sathyajith Bhat
2
@vbNewbie 请提供更多细节 - 这是您正在构建的应用程序吗?您的系统规格是什么 - 如处理器,RAM数量等。 - Sathyajith Bhat
什么类型的程序?asp.net、winforms、控制台... - CodesInChaos
@Sathya - 我在发表评论后立即发现了这一点,并进行了更正 =) - Tomas Aschan
显示剩余2条评论
5个回答

9
Split 方法为返回的数组中的每个元素分配字符串对象的内存。如果您遇到了内存问题,那么问题就出在这一行上。由于您一次性为潜在的 10,000 个大字符串分配内存,而且堆可能是碎片化的,所以不足为奇,您可能会遇到无法找到足够连续空间来分配下一个字符串的情况(这将导致您遇到的异常)。您真的需要所有这些字符串吗?或者您可以像这样循环执行某些操作:
  1. 逐行读取(或读入缓冲区)以构建字符串。
  2. 当找到第一个“块”时停止。
  3. 从块中获取所需信息。(我假设您不需要整个字符串,只是想从中获取一些内容?)
  4. 继续读取 html(重新开始 #1),将其读入同一字符串变量中,以便第一个块可以进行垃圾回收。
如果您能以这种方式实现解决方案,您的问题应该就消失了。

5
请注意,OOM并不意味着您的物理RAM用完了,而是无法将所需大小的新内存块放入地址空间中。可能有多种原因导致这种情况发生:
- 堆碎片化 - 32位进程的2GB限制 - 在配置文件中设置的最大内存大小
此外,还有一些假的OOM异常。例如,GDI + 对于包括无效参数在内的广泛问题返回内存不足错误。这些也会转换为OOM异常。

那么你认为通过改变代码并使用更有效、高效的逻辑可以解决这个问题吗? - vbNewbie
好的,我已经确定问题出现在以下这行代码中 - sampleResults = _html.split(stringsplitter,stringsplitoptions.removeemptyentryies)。 - vbNewbie

2
你尝试过使用.NET性能计数器来分析应用程序运行时的内存使用情况吗?你可以启动性能监视器(在Windows 2008 R2中,它位于管理工具>性能监视器下),并在.NET Memory下添加Gen 0 Heap Size、Gen 1 Heap Size、Gen 2 Heap Size和Large Object Heap size计数器。随着应用程序的运行,你可以使用这些计数器来查看内存分配的位置。
由于你正在处理字符串,因此重要的是要考虑到超过85,000字节的字符串将被分配在大对象堆上。所有其他堆都会被垃圾回收器压缩,但出于性能原因,大对象堆永远不会被压缩。这基本上意味着对象将被清除,从LOH中释放,但剩余对象从其原始位置移动到LOH。CLR的这种行为可能会导致LOH碎片化,最终导致OOM异常。
这里有一个简单的例子,说明LOH(大对象堆)碎片化如何导致OOM(内存不足)异常。假设我们在LOH上有100个单位可用,我们先分配10个单位,然后再分配5个单位,剩余85个单位。由于CLR必须连续分配内存,所以现在我们占据了LOH的前15个单位。我们尝试为一个非常大的对象分配86个单位,结果导致OOM异常。意识到没有足够的空间,我们清除了最初分配的10个单位,现在总共有95个单位可用。我们再次尝试分配86个单位,但由于CLR不会压缩LOH且CLR要求连续的86个单位,所以仍然会出现OOM异常。只有在清除第二个5个单位之后,我们才能分配86个单位。这是一个过度简化的例子,但如果你搜索LOH碎片化或查看article,你可以得到更深入的解释。
我不能确定你遇到的问题是什么,但我肯定会检查性能监视器以查看你的内存分配情况。如果你确定你正在分配超过85,000字节的字符串或其他对象,则尝试进一步拆分它们,使它们不要进入大对象堆(LOH)。

1

我也遇到了同样的问题。 考虑转移到64位操作系统。 我有一些技巧来延迟这个异常:

1. 最重要的是,在处理字符串(特别是长字符串)时,使用ref从一个方法传递到另一个方法。 它显著地减少了内存和性能。

2. 您可以使用AppDomain来存储数据。 这将使您的内存容量翻倍。

3. 只要可能,就不要创建对象和字符串(Web请求/响应),而是一直使用相同的MemoryStream或缓冲区。 分配一个最大可能大小的缓冲区(甚至*2最大预期大小)。 使用new分配(Stream/StringBuilder/字符串/类/缓冲区) 会使堆栈过度拥挤。


0

3
虽然这个链接可能回答了问题,但最好在此处包含答案的必要部分,并提供该链接作为参考。仅包含链接的答案如果链接页面发生更改,可能会变得无效。 - bensiu
2
链接现在已经失效了。@bensiu的警告是准确的。 - Chris Bennet

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接