System.OutOfMemoryException 發(fā)生的兩種情況
1)應(yīng)用程序消耗了過(guò)多的內(nèi)存
2)內(nèi)存碎片過(guò)多
故障解決:
a)增加w3wp進(jìn)程可以使用的內(nèi)存空間
打開(kāi)boot.ini的 /3GB開(kāi)關(guān)
[boot loader]
timeout=30
default=multi(0)disk(0)rdisk(0)partition(2)\WINNT
[operating systems]multi(0)disk(0)rdisk(0)partition(2)\WINNT="????" /3GB
B)減少內(nèi)存分配,避免內(nèi)存碎片的出現(xiàn)
修改DAO基類,將dataset的RemotingFormat ,
設(shè)為SerializationFormat.Binary
我用C#做數(shù)據(jù)庫(kù)應(yīng)用的時(shí)候,經(jīng)常遇到的一個(gè)問(wèn)題就是內(nèi)存消耗特別大。這種情
況在作大數(shù)據(jù)量的數(shù)據(jù)庫(kù)導(dǎo)入導(dǎo)出的時(shí)候更為明顯。
說(shuō)一個(gè)常見(jiàn)的數(shù)據(jù)庫(kù)導(dǎo)入導(dǎo)出的Case, 大家看看比較合理的辦法是什么:
環(huán)境:
有一個(gè)數(shù)據(jù)庫(kù)SRC, 里面有500萬(wàn)條記錄。有A, B, C三個(gè)字段。數(shù)據(jù)庫(kù)的大小去掉水分后,大約500MB的數(shù)量級(jí)。這個(gè)作為源數(shù)據(jù)庫(kù)。數(shù)據(jù)庫(kù)DEST是目標(biāo)數(shù)據(jù)庫(kù),里面有AA, BB, CC, DD, EE, FF等5個(gè)字段。其中,AA, BB, CC的值,是根據(jù)SRC.A和B的值計(jì)算得來(lái)的。而且過(guò)程復(fù)雜,所以不適合使用存儲(chǔ)過(guò)程實(shí)現(xiàn)。并且AA, BB, CC不是單獨(dú)得出,而是同時(shí)以SRC.A和SRC.B得出的(當(dāng)然,運(yùn)算三遍可以分別取到各值,但顯然運(yùn)算量就大了3倍)。目的:
將SRC中的所有數(shù)據(jù)經(jīng)過(guò)變換,導(dǎo)入到數(shù)據(jù)庫(kù)DEST中。我所嘗試過(guò)的方法:
1. 最開(kāi)始用最簡(jiǎn)單的DataAdapter.Fill(DataTable)的辦法。
然后
foreach(DataRow rowsrc in dtSrc){
DataRow row = dtDest.NewRow();
....
dtDest.Rows.Add(row);
}
daDest.Update(dtDest);
dtDest.AcceptChanges();
這明顯不是處理大數(shù)據(jù)量遷移的辦法。
最明顯的問(wèn)題是內(nèi)存消耗極大。500MB的數(shù)據(jù)庫(kù)表變成DataSet中的DataTable,
體積括大了很多。再加上dtDest中的拷貝,還有daDest.Update所占用的資源,
很容易就超過(guò)了2GB的應(yīng)用程序內(nèi)存地址空間,而報(bào)錯(cuò)退出了。
2. 我嘗試用DataReader讀入數(shù)據(jù),然后寫(xiě)進(jìn)dtDest
using( ... reader = ...){
while(reader.Read()){
DataRow row = dtDest.NewRow();
....
dtDest.Rows.Add(row);
}
}
daDest.Update(dtDest);
dtDest.AcceptChanges();內(nèi)存占用少了很多,因?yàn)镾RC讀入DataReader的數(shù)據(jù),似乎會(huì)因?yàn)橹蟛挥昧耍?br />而有所釋放。但是已經(jīng)進(jìn)入dtDest的數(shù)據(jù)沒(méi)有必要存儲(chǔ)在內(nèi)存中的道理。可是又
沒(méi)有辦法。即使daDest.Update和dtDest.AcceptChanges()后,它依然占用這內(nèi)
存。因此將daDest.Update和dtDest.AcceptChanges()放入循環(huán)內(nèi),并不能對(duì)內(nèi)
存使用有所改善。
3. 純SQL語(yǔ)句。不用DataTable
用SqlCommand.ExecuteNonQuery()的來(lái)執(zhí)行"Insert Into ..."的SQL語(yǔ)句。這樣
作后,發(fā)現(xiàn)內(nèi)存占用大大降低了。起到了想達(dá)到的“管道”的效果。可是最之而來(lái)
的確實(shí)更嚴(yán)重的問(wèn)題。這樣做的磁盤(pán)空間占用極大。數(shù)據(jù)庫(kù)會(huì)產(chǎn)生大量的日志,
和廢棄空間。以Access為例,程序運(yùn)行一段時(shí)間后,就超過(guò)了2GB最大文件的限
制。當(dāng)執(zhí)行“壓縮和恢復(fù)數(shù)據(jù)庫(kù)”后,只有100MB左右。可見(jiàn)產(chǎn)生的數(shù)據(jù)垃圾多
大。當(dāng)然,采用SQL Server會(huì)沒(méi)有2GB的限制,但是這么大量的日志和廢棄空
間,絕對(duì)不是一個(gè)適合的結(jié)果。而且,如果安這種比例,一個(gè)500MB的數(shù)據(jù)庫(kù),
將占用超過(guò)10GB的存儲(chǔ)空間,這是一個(gè)太大的浪費(fèi)了。當(dāng)然事后可以壓縮,但是
處理過(guò)程中的空間浪費(fèi)不容忽視。
4. 不輸出到數(shù)據(jù)庫(kù),以CSV輸出到文本文件
這是個(gè)辦法,至少一來(lái)不占用什么內(nèi)存,二來(lái),也沒(méi)有浪費(fèi)硬盤(pán)空間。但是麻煩
的是,丟失了最重要的類型信息。對(duì)于字符串和數(shù)字還好說(shuō),最多就是字符串沒(méi)
有了長(zhǎng)度,數(shù)字沒(méi)有了精度,但都還能夠保存下來(lái)。比較麻煩的是有些復(fù)雜類
型,比如IMAGE, Binary,和GUID等,這種字節(jié)流形式的類型是沒(méi)有辦法報(bào)存在
CSV中的。
5.導(dǎo)出到XML
當(dāng)然用XML好一些,但是其Binary存儲(chǔ)的Parse也是很耗費(fèi)資源的,而且,用XML
如何才能導(dǎo)入到數(shù)據(jù)庫(kù)中呢?用C#就又回到老路上來(lái)了。SQL Server支持XML,
但是其他的數(shù)據(jù)庫(kù)呢?例如Access? 畢竟不是所有人都買(mǎi)的起SQL Server或者所
有場(chǎng)合都適于使用SQL Server的。
對(duì)于這個(gè)問(wèn)題大家有什么好辦法么?我覺(jué)得操作大一點(diǎn)的數(shù)據(jù)庫(kù)的時(shí)候,內(nèi)存占
用是個(gè)很明顯的問(wèn)題。也許有很簡(jiǎn)單的方法我沒(méi)有考慮到而走了很多彎路。謝謝了。