关于dictionary过大,造成内存溢出的解决方法

马歌 2009-08-10 03:22:04
任务背景:海量数据的信息检索.处理数据总规模我们目前大概是20T,需要对这20T数据进行索引,然后构造一个简易的检索系统.
现在的问题:
假设有一个大文件1T左右,里面每一行是一个单词,任务很简单,统计一下,每个单词出现了多少次,存储下来。

我现在的实现大概是这样的

Dictionary <string,int> a=new Dictionary <string,int>();

while 循环读入 word
{
object b;
b=a[word];
if (b==null)
a.add(word)
else
a[word]++;
}
在处理了大概20G左右数据以后,内存溢出了。

{"引发类型为“System.OutOfMemoryException”的异常。"}
{System.Collections.ListDictionaryInternal}
{System.Collections.ListDictionaryInternal.NodeKeyValueCollection}


{Void Resize()}
System.Reflection.MethodAttributes.Private
|System.Reflection.MethodAttributes.HideBySig

分析应该是dictionary在冲突发生比较大的时候进行扩容,此时可用内存耗光或者超过了2G的限制。
我现在的疑问是
问题1: 2千多万个词,实际上存储的话要几十M就应该够了,为什么我发现占用的内存会很高(占用物理内存就1.5G)呢?远远要超出预估算的值,各位可以简单写个代码往里面存点数据就会发现其实是非常消耗内存的?why?
问题2:如果确实超过内存,那么这项工作该如何实现呢?
...全文
2381 38 打赏 收藏 转发到动态 举报
AI 作业
写回复
用AI写文章
38 条回复
切换为时间正序
请发表友善的回复…
发表回复
wangshuhang2000 2012-06-19
  • 打赏
  • 举报
回复
楼主最后没有汇报呢
codingonline 2011-03-15
  • 打赏
  • 举报
回复
楼主最后没有汇报呢
  • 打赏
  • 举报
回复
[Quote=引用 23 楼 guoyichao 的回复:]
Dictionary初始的时候是按Capacity占用内存的,当达到上限则会增长到至少2倍的内存,所以可能包含的数据量不大,但占用的内存却非常多。
[/Quote]

是滴!Dictionary<K,T>当扩大规模时,每一次都是扩大一倍,而不是一个单元一个单元扩大。

实际上还可以使用 SortedDictionary<TKey, TValue> 来牺牲一点(微不足道的)插入时间来换取最好的查询时间和存储空间。

如果还超过内存空间,看起来只有将任务切分为多趟、每一趟使用磁盘来保存中间结果才可以。
tristan0429 2009-08-16
  • 打赏
  • 举报
回复
这种任务是一次性的还是每周都要做一下,总得给个情况吧,20T什么之类的都不是什么问题,关键是你时间上的安排。人家有些解密算法光运算都要几个月呢
马歌 2009-08-16
  • 打赏
  • 举报
回复
[Quote=引用 34 楼 sp1234 的回复:]
引用 23 楼 guoyichao 的回复:
Dictionary初始的时候是按Capacity占用内存的,当达到上限则会增长到至少2倍的内存,所以可能包含的数据量不大,但占用的内存却非常多。


是滴!Dictionary <K,T>当扩大规模时,每一次都是扩大一倍,而不是一个单元一个单元扩大。

实际上还可以使用 SortedDictionary <TKey, TValue> 来牺牲一点(微不足道的)插入时间来换取最好的查询时间和存储空间。

如果还超过内存空间,看起来只有将任务切分为多趟、每一趟使用磁盘来保存中间结果才可以。
[/Quote]

呵呵,我试试sorteddictionary,回头跟大家汇报结果。
马歌 2009-08-16
  • 打赏
  • 举报
回复
[Quote=引用 33 楼 tristan0429 的回复:]
这种任务是一次性的还是每周都要做一下,总得给个情况吧,20T什么之类的都不是什么问题,关键是你时间上的安排。人家有些解密算法光运算都要几个月呢
[/Quote]

时间上两天能处理24T就可以。任务可以看作是一次性的。
马歌 2009-08-16
  • 打赏
  • 举报
回复
[Quote=引用 31 楼 sbwwkmyd 的回复:]

hash就是这么占空间,这么大的数据量,hash冲突也很厉害,这种情况用Dictionary,时间与空间效率都不合算(由于冲突问题,上十万的数据就不要用hash了)。
如果是单词数2千多万,存储到硬盘上才300M,建议用B树结构,既能快速查询与修改,占用的空间又不会有Dictionary那么夸张。
[/Quote]

b树C#中有现成的类吗?
showjim 2009-08-16
  • 打赏
  • 举报
回复
[Quote=引用 30 楼 lover6 的回复:]
引用 29 楼 honkerhero 的回复:
把上边的方法综合一下,不要一次一更新,攒个1000-2000个记录到数据库更新一下

比如,DIC里面有了1000条记录,就序列化成XML到数据库里更新一下,然后清空DIC
等下次满了1000再更新一下

1000只是预计值,我估计如果允许,能几千更新一次


数据量在这摆着,再好的办法也能用不少时间


海量数据是真的,不过其实出现的词并不多,存下来估计也就2个G左右(这个我可以考虑用64位系统,8G内存搞定),问题是现在实验阶段一个文件,单词数2千多万,存储到硬盘上才300M,占用的内存就能达到2G,这是为什么呢?
[/Quote]
hash就是这么占空间,这么大的数据量,hash冲突也很厉害,这种情况用Dictionary,时间与空间效率都不合算(由于冲突问题,上十万的数据就不要用hash了)。
如果是单词数2千多万,存储到硬盘上才300M,建议用B树结构,既能快速查询与修改,占用的空间又不会有Dictionary那么夸张。
马歌 2009-08-15
  • 打赏
  • 举报
回复
[Quote=引用 29 楼 honkerhero 的回复:]
把上边的方法综合一下,不要一次一更新,攒个1000-2000个记录到数据库更新一下

比如,DIC里面有了1000条记录,就序列化成XML到数据库里更新一下,然后清空DIC
等下次满了1000再更新一下

1000只是预计值,我估计如果允许,能几千更新一次


数据量在这摆着,再好的办法也能用不少时间
[/Quote]

海量数据是真的,不过其实出现的词并不多,存下来估计也就2个G左右(这个我可以考虑用64位系统,8G内存搞定),问题是现在实验阶段一个文件,单词数2千多万,存储到硬盘上才300M,占用的内存就能达到2G,这是为什么呢?
honkerhero 2009-08-14
  • 打赏
  • 举报
回复
把上边的方法综合一下,不要一次一更新,攒个1000-2000个记录到数据库更新一下

比如,DIC里面有了1000条记录,就序列化成XML到数据库里更新一下,然后清空DIC
等下次满了1000再更新一下

1000只是预计值,我估计如果允许,能几千更新一次


数据量在这摆着,再好的办法也能用不少时间
马歌 2009-08-14
  • 打赏
  • 举报
回复
[Quote=引用 25 楼 bwangel 的回复:]

此言差矣,我并没有让你把20T的数据全部导入数据库然后再汇总,那是白痴.

我只是建议你把内存中的Dictionary转换成数据库表结构KeywordStats(Keyword, WordCOUNT),再建立好关于keyword字段的索引.
然后从你要统计的文件中一次读入一行,在数据库中如下更新:
然后用Update KeyWordStats SET WordCOUNT=WordCOUNT+1 WHERE Keyword='关键字'更新数据库.

如果你认为建立数据库,连硬盘都撑不下, 难道内存就能撑下了?!
[/Quote]

呵呵,上百亿次的update?我觉得还是不行的。你可以写个循环update一下试试
Lovely_baby 2009-08-11
  • 打赏
  • 举报
回复
直接用数据库来统计 试试
赵牧野 2009-08-11
  • 打赏
  • 举报
回复
有意思,非实践的人不会遇到
十八道胡同 2009-08-11
  • 打赏
  • 举报
回复
用数据库来统计,
建个表就是了
angieduan 2009-08-11
  • 打赏
  • 举报
回复
我估计LZ那些数据不是在数据库里的,导入一次数据库估计要很久很久。。。
bwangel 2009-08-11
  • 打赏
  • 举报
回复
直接用数据库来统计不是很容易吗?永远不存在内存问题.
nyq1999 2009-08-11
  • 打赏
  • 举报
回复
用lucene吧,数据量太大Dictionary吃不消,耗内存的问题嘛,我之前也测试过,如果Key是字符串,而且比较长的话,吃内存较厉害,如果是int效果好些
angieduan 2009-08-11
  • 打赏
  • 举报
回复
这样写效率肯定不高,而且产生的文件会很多,但是非常方便。。。
angieduan 2009-08-11
  • 打赏
  • 举报
回复
我有个思路:
1. 新建一个Folder,比如Result
2. 你就不要用Dict了,直接用txt文件来存储
3. Dictionary <string, int>, string对应文件名,int对应txt文件内容
4. 这样你拿到一个string直接可以用文件路径去查找,有这个文件就把其txt内容取出+1,没有这个文件,就新建,内容为1

LZ你懂我的意思吧?
angieduan 2009-08-11
  • 打赏
  • 举报
回复
[Quote=引用 12 楼 ttianqq 的回复:]
我记得Dictionary是有大小限制的,忘了上限是多少了
像你这种情况,可以试试这样

Dictionary <string, Dictionary <string,int>>
将你要处理的内容,合理的处理一下,放到这里面  就不存在溢出问题了
[/Quote]
这样写也没用的,32bit下单个App的内存就限制在2GB。
加载更多回复(18)

111,094

社区成员

发帖
与我相关
我的任务
社区描述
.NET技术 C#
社区管理员
  • C#
  • AIGC Browser
  • by_封爱
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告

让您成为最强悍的C#开发者

试试用AI创作助手写篇文章吧