一個無法捕獲ADO.NET Dataset的記憶體錯誤

來源:互聯網
上載者:User

Dataset是ADO.NET在記憶體儲存資料所用的新結構。在某些方面上,Dataset和ADO的Recordset對象相似;不過,Dataset可以把整個schema(包括table、關係、關鍵字連同真實資料)儲存在記憶體中,在這一點上,Dataset比Recordset功能更強。因此,你可以查詢和修改Dataset而不必擔心影響到正在使用它的資料庫。

當考慮到Dataset把它的全部資料放在記憶體中,有些人會擔心如果記憶體耗盡了會出現什麼問題。做一個合理的猜測很容易,但是實際情況可能要比你猜測的要複雜一些。為了示範一下.NET在這種情況下會有什麼動作,我先說說如何建立一個不停向一個Dataset中載入資料直到記憶體耗盡的項目。注意我們並不推薦這個過程,只是向你證明耗盡記憶體是多麼容易的一件事。

建立TooMuchData項目

建立一個不停地向一個Dataset載入資料的項目很容易。開啟Visual Studio .NET並建立一個新的VB.NET視窗應用程式。向視窗(form)中添加一個按鈕控制項並雙擊它,這樣就開打了它的代碼視窗。在代碼視窗中填寫下列代碼:

Private Sub Button1_Click(ByVal sender As System.Object, _ByVal e As System.EventArgs) Handles Button1.ClickDim sConnString As String = "Server=localhost;Database=pubs;uid=sa;pwd=;"Dim sSQL As String = "SELECT * FROM authors"Dim daProduct As SqlDataAdapter = New SqlDataAdapter(sSQL, sConnString)Dim myDS As New DataSet()Do While TruedaProduct.Fill(myDS, "authors")If myDS.Tables("authors").Rows.Count Mod 100 = 0 ThenDebug.WriteLine(myDS.Tables("authors").Rows.Count.ToString())End IfLoopEnd Sub

代碼的第三行建立字串到變數sConnString的聯結。如果你想重建立立這個項目,你或許需要修改這一行的代碼,除非你在伺服器上運行它並有一個"sa/no password"使用者ID和相應的口令。

代碼的第四行建立一個簡單的SQL查詢。一般不推薦Select *這樣的用法,但是這裡沒有什麼問題,因為我們的目標就是抓到儘可能多的記錄。選擇的表格(table)的首要(primary)關鍵字域也不是問題,因為我們建立的Dataset是weakly type而且用來載入資料的DataTable的首要關鍵字域也沒有設定。

下兩行代碼建立DataAdapter和Dataset對象。然後進入一個死迴圈,在死迴圈中調用DataAdapter的Fill方法並把記錄加入稱之為“authors”的DataTable中。迴圈包括一個If語句用來顯示行數的當前值是否可以被100整除。這不是不必可少的,但是它可以有兩個方面的作用:首先,你可以知道程式依然在運行;其次,你可以知道大概有多少個記錄加到Dataset中去了。

運行本程式
當你建立本程式後,你可能希望在運行它之前作些修改。當Dataset越來越大時,它將消耗越來越多的記憶體。一旦它耗盡所有可用記憶體,Windows就開始把記憶體交換到記憶體分頁檔中。在許多機器上,分頁檔是相當大的,所以本程式可能要運行好幾個小時。例如,我在測試本程式時,用的是600-MHz PIII CPU和512-MB RAM的膝上型電腦。頁分頁檔設定為最小值以縮短程式已耗用時間——這是儘快完成測試的訣竅。即使這樣,本程式在塞滿所有的可用記憶體之前還是運行了幾十分鐘或者若干小時。

現在你可以運行本程式並點擊按鈕控制項來開始處理過程。它建立了一個到伺服器的聯結,從表格中讀取資料並傳到Dataset中的DataTable對象。同樣的記錄也儲存到該Dataset直到記憶體最終耗盡。你可以通過工作管理員來觀察記憶體的使用方式,你甚至可以看到可用記憶體的隨著記憶體和磁碟的資料交換而增減的情況。在我的機器中,Dataset在機器耗盡記憶體前已經長到1400000條記錄的規模。

耗盡記憶體
一旦你耗盡記憶體後會發生什麼取決你是在VS.NET環境下運行本程式還是運行本程式的編譯版。如果你啟動並執行是本程式的編譯版並且沒有進行錯誤處理(error handling),你在程式運行中不會發現錯誤,它僅僅是在記憶體耗盡時停止運行。如果你在VS.NET環境下運行代碼並且沒有進行錯誤處理,程式將會停止運行並在調試視窗下出現下面的錯誤資訊:

Fatal out of memory error.
The program '[2340] TooMuchData.exe' has exited with code 0 (0x0).

因此,你可能會開始嘗試添加一個錯誤處理函數來檢測System.OutOfMemoryException的情況。例如,你可能用一個Try…Catch語句來觀察是否出現例外。一個比較通用的方法如下:

Do While TrueTrydaProduct.Fill(myDS, "authors")If myDS.Tables("authors").Rows.Count Mod 100 = 0 ThenDebug.WriteLine(myDS.Tables("authors").Rows.Count.ToString())End IfCatch ex As ExceptionMessageBox.Show("Error: " & ex.Message)End TryLoop

不幸的是,這種方法根本就不工作。在MessageBox.Show語句上設定一個斷點,理論上當運行到這個語句上應該出現,但是這一點永遠也不會到達。當最終耗盡記憶體時,調試視窗出現的卻是下面的資訊:

Fatal out of memory error.
An unhandled exception of type 'System.OutOfMemoryException' occurred in system.data.dll

如果是MessageBox出現該訊息情況會好些,但是相反,VS.NET(或者Framework)產生並顯示了上述訊息,跟蹤這個特定的錯誤並不是有效解決方案。

如果在本程式中添加錯誤處理並編譯運行它,那麼你將得到另外一種結果。這次,你會發現MessageBox報告說程式遇到了一個無法處理的錯誤,類型為System.OutOfMemoryException,位於system.data.dll。然而,這個MessageBox來自Framework而不是你自己用代碼編寫的MessageBox。

並不象聽起來那麼容易
你可以建立一個可以消耗所有可用記憶體的Dataset,但是消耗的過程並不簡單,它需要大量的記錄,尤其是大量的時間。Dataset可能需要幾個小時才能填滿記憶體,幾乎沒有什麼應用程式可以在這種情況下運行很長時間,這就產生了問題。當然,每台機器的配置都不盡相同,如處理器速度、記憶體容量以及分頁檔大小,但是這種結果的出現都是不受歡迎的。

無論這種結果是如何令人討厭,錯誤都應該可以被捕獲。不幸的是,我們在這種情況下無法捕獲到System.OutOfMemoryException錯誤,也就是說我們在應用程式中很難處理這種特定錯誤。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.