.net OutOfMemory异常

时间:2009-05-21 12:07:34

标签: .net string windows-mobile memory-management compact-framework

对我正在为Windows Mobile编写的类库进行一些最终测试 (使用Compact Net Framework 2.0),我遇到了一个OOM异常。

基本上,我的库首先加载一个字典文件(带有单词列表的普通文本文件),然后加载另一个基于字典的文件(我称之为KeyMap),其大小与先前加载的字典大致相同。

一切正常(使用模拟器和我的真实设备)上面的文件,直到我尝试加载大小约为2.7MB的西班牙语词典。到目前为止,我使用的其他语言词典没有任何OOM例外,每个约为1.8MB。使用西班牙语字典,我可以毫无问题地加载第一个文件但是当我尝试读取第二个文件时,我收到了OOM错误。

下面我写了我正在使用的代码。基本上我读取文件并将其内容分配给字符串变量(DictData和TextKeyMap)。然后我在字符串变量上创建一个Split,将内容传递给字符串数组(Dict和KeyMap)。

'Loading Dictionary works
Dim ReadDictionary As StreamReader = New StreamReader(DictPath, Encoding.UTF8)
   DictData = ReadDictionary.ReadToEnd()
            ReadDictionary.Close()
            Dict = DictData.ToString.ToUpper.Split(mySplitSep.ToCharArray) 'mySplitSep=chr(10)
            DictData = "" 'perhaps "nothing" is better

 'Loading KeyMap gives me error
 Dim ReadHashKeyMap As StreamReader = New StreamReader(HashKeyMapPath, Encoding.UTF8)
    TextKeyMap = ReadHashKeyMap.ReadToEnd() '<-- OOM-error
            ReadHashKeyMap.Close()
            KeyMap = TextKeyMap.ToString.Split(mySplitSep.ToCharArray) 'mySplitSep=chr(10)
            TextKeyMap = "" 'perhaps "nothing" is better 

我是一名没有专家知识的业余程序员,所以上面显示的代码可能是 改进。我没有使用ReadToEnd,而是尝试读取For循环中的每一行,但我得到了 同样的错误(它也慢了)。

我认为错误是由于Windows Mobile中32MB连续内存的限制。

你们中的任何人都可以通过建议一些替代解决方案来帮助我吗?也许 问题是由于我上面显示的糟糕代码?怎么样,加载第二个文件 另一个线程?这可行吗?

我将非常感谢所有帮助。

编辑:我前段时间问了一个类似的问题(here),但是那个问题与处理字节的接收问题更相关,并且使用了块来解决。在这种情况下,我正在处理字符串。

Edit2 :此库是一个拼写检查库。它运行良好,并实现了一些非常先进的技术,如Soundex和DoubleMetaPhone算法。到目前为止唯一的主要问题是上面提到的问题,有一个庞大的西班牙语文本文件。其他词典也行。有关详细信息,请参阅this link

3 个答案:

答案 0 :(得分:3)

因为你没有说你正在使用这个文件,我假设你只是因为某些原因搜索一个单词。

首先,尝试将完整文件加载到内存中可能不是一个好主意。相反,搜索文件以获取所需的数据(字)可能会更高效,并且可能在内存中保留某种索引信息以加快速度。

由于您尝试搜索的数据只是一个单词列表,因此最好扫描文件并在字典中记录单词的第一个字母发生变化。例如A从第0行开始; B从第200行开始; C从300行开始等。使用这两条信息来填充你的字典;字母是键,行号是值。实际上,字典成为单词列表文件的高级索引。这本字典也很小。

然后,当您开始搜索单词时,请使用单词的第一个字母来搜索词典。这将获得行号,其中以该字母开头的单词位于文件中。使用行号(重新)打开文件,然后通过将流指针移动到目标行直接转到word文件中的那一行。然后从那里搜索目标词。要么按顺序搜索,要么一次搜索一行(不推荐它会很慢,但会更容易编码)。或者,使用binary chop搜索单词(更快,但更难编码)。虽然对于后者,您还需要知道以目标字母开头的单词在文件中停止的位置,因为您将搜索文件的一部分。我还建议您在文件中执行单词搜索,而不是将所有这些单词加载到内存中,否则您可能会回到开始出现OOM错误的位置。

如果您不确定,请在此处发表评论,我会尽力回答。

祝你好运

答案 1 :(得分:2)

您似乎没有足够的内存来同时保留内存中所有文件的所有文本。您可能需要提出一种策略来缓存文件的有限子集,并且足够智能,以便在请求的内容不在缓存中时返回到文件。

如果练习的全部内容是您不必返回文件(例如,建立某种索引),您也可以尝试“聪明”并提出替代表示对于利用大多数西方语言的非常可压缩性质的内存文本。

答案 2 :(得分:2)

我会说有问题的一行是这个:

Dict = DictData.ToString.ToUpper.Split(mySplitSep.ToCharArray)

GC无法跟上简单线后面的临时对象的创建。 “ToUpper”正在创建原始字符串的副本,“Split”正在从该副本中创建一个新数组(并且可能使用更多内存用于拆分算法本身)。顺便说一句,对“ToString”的调用是没用的,DictData已经是一个字符串,对吗?

就个人而言,我会通过块读取流,并按片断分割成List&lt;&gt;。但是如果你想保持你的代码简短,试试这个,你永远不会知道:

DictData = ReadDictionary.ReadToEnd()
ReadDictionary.Close()
DictData = DictData.ToUpper()
GC.Collect()
Dict = DictData.Split(mySplitSep.ToCharArray)
DictData = Nothing
GC.Collect()

我从未发现调用GC.Collect是一个很好的解决方案。称这通常意味着“应该做得更好”。但是.NET CF下的内存管理有时很痛苦。