我運行到我想不通奇怪的事情字符串時。我有一個存儲在ntext字段中的一堆報表的SQL表。當我將其中一個文件的值複製並粘貼到記事本中並將其保存(使用Visual Studio從不同行中的較小報告中獲取值)時,原始txt文件大約爲5Mb。當我嘗試使用SqlDataReader獲取相同的數據並將其轉換爲字符串時,出現內存不足異常。這裏是我正在試圖做到這一點:內存讀取從SqlDataReader的
string output = "";
string cmdtext = "SELECT ReportData FROM Reporting_Compiled WHERE CompiledReportTimeID = @CompiledReportTimeID";
SqlCommand cmd = new SqlCommand(cmdtext, conn);
cmd.Parameters.Add(new SqlParameter("CompiledReportTimeID", CompiledReportTimeID));
SqlDataReader reader = cmd.ExecuteReader();
while (reader.Read())
{
output = reader.GetString(0); // <--- exception happens here
}
reader.Close();
我試圖創建一個對象和一個StringBuilder來獲取數據,但我仍然得到同樣的內存溢出異常。我也嘗試使用reader.GetValue(0).ToString()以及無濟於事。該查詢只返回1行,並且當我在SQL Management Studio中運行它時,它會盡可能快樂。
引發的異常是:
System.OutOfMemoryException was unhandled by user code
Message=Exception of type 'System.OutOfMemoryException' was thrown.
Source=mscorlib
StackTrace:
at System.String.CreateStringFromEncoding(Byte* bytes, Int32 byteLength, Encoding encoding)
at System.Text.UnicodeEncoding.GetString(Byte[] bytes, Int32 index, Int32 count)
at System.Data.SqlClient.TdsParserStateObject.ReadString(Int32 length)
at System.Data.SqlClient.TdsParser.ReadSqlStringValue(SqlBuffer value, Byte type, Int32 length, Encoding encoding, Boolean isPlp, TdsParserStateObject stateObj)
at System.Data.SqlClient.TdsParser.ReadSqlValue(SqlBuffer value, SqlMetaDataPriv md, Int32 length, TdsParserStateObject stateObj)
at System.Data.SqlClient.SqlDataReader.ReadColumnData()
at System.Data.SqlClient.SqlDataReader.ReadColumn(Int32 i, Boolean setTimeout)
at System.Data.SqlClient.SqlDataReader.GetString(Int32 i)
at Reporting.Web.Services.InventoryService.GetPrecompiledReportingData(DateTime ReportTime, String ReportType) in C:\Projects\Reporting\Reporting.Web\Services\InventoryService.svc.cs:line 3244
at SyncInvokeGetPrecompiledReportingData(Object , Object[] , Object[])
at System.ServiceModel.Dispatcher.SyncMethodInvoker.Invoke(Object instance, Object[] inputs, Object[]& outputs)
at System.ServiceModel.Dispatcher.DispatchOperationRuntime.InvokeBegin(MessageRpc& rpc)
InnerException:
null
我曾與這似乎工作的其他行編號進行測試,但是這是一個假陽性那些測試ID的沒有數據。在查看包含接近相同報告的表格後,我拉了一些其他測試ID,我得到了相同的異常。也許它的字符串是如何編碼的?存儲在表中的數據是一個JSON編碼的字符串,它是由我在其他地方製作的真正粗糙的類生成的,以防有所幫助。
這裏是前一碼塊:
// get the report time ID
int CompiledReportTimeTypeID = CompiledReportTypeIDs[ReportType];
int CompiledReportTimeID = -1;
cmdtext = "SELECT CompiledReportTimeID FROM Reporting_CompiledReportTime WHERE CompiledReportTimeTypeID = @CompiledReportTimeTypeID AND CompiledReportTime = @ReportTime";
cmd = new SqlCommand(cmdtext, conn);
cmd.Parameters.Add(new SqlParameter("CompiledReportTimeTypeID", CompiledReportTimeTypeID));
cmd.Parameters.Add(new SqlParameter("ReportTime", ReportTime));
reader = cmd.ExecuteReader();
while (reader.Read())
{
CompiledReportTimeID = Convert.ToInt32(reader.GetValue(0));
}
reader.Close();
CompiledReportTypeIDs是一個字典,獲取基於人提供在方法的開始供入的字符串參數的正確CompiledReportTimeTypeID。 ReportTime是一個早先提供的日期時間。
編輯: 爲了排除SQL數據類型問題,我將刪除表並使用ReportData字段將其重新創建爲nvarchar(MAX)而不是ntext。這是一個長鏡頭,我會再次更新我發現的內容。
EDIT2: 更改在表中爲nvarchar(最大)領域沒有任何影響。我也嘗試使用output = cmd.ExecuteScalar()。ToString()以及沒有影響。我試圖看看SqlDataReader是否有最大尺寸。當我從SQL Mgmt Studio中複製文本的值時,它在保存在記事本中時只有43Kb。爲了驗證這一點,我用一個已知的工作ID(一個較小的報告)提取了一份報告,當我將這個值直接從Visual Studio中拷貝出來並將它轉儲到記事本中時,它大約爲5MB!這意味着這些大的報告可能位於nvarchar(max)字段的〜20MB範圍內。
EDIT3: 我重新啓動一切,包括我的dev IIS服務器,SQL服務器和我的dev的筆記本電腦。現在它似乎在工作。這不是爲什麼發生這種情況的答案。我將這個問題留待解釋發生了什麼,我將其中的一個標記爲答案。
Edit4: 話雖如此,我跑另一個測試不改變的事情,同樣的異常又回來了。我真的開始認爲這是一個SQL問題。我正在更新這個問題上的標籤。我做了一個單獨的應用程序,運行完全相同的查詢,它運行良好。
Edit5: 我已經實現順序訪問按下面的答案之一。一切都被正確讀入流,但是當我嘗試寫出一個字符串時,我仍然遇到了內存不足異常。這是否表明獲得連續的內存塊的問題?這裏是我是如何實現緩衝:
reader = cmd.ExecuteReader(CommandBehavior.SequentialAccess);
long startIndex = 0;
long retval = 0;
int bufferSize = 100;
byte[] buffer = new byte[bufferSize];
MemoryStream stream = new MemoryStream();
BinaryWriter writer = new BinaryWriter(stream);
while (reader.Read())
{
// Reset the starting byte for the new CLOB.
startIndex = 0;
// Read bytes into buffer[] and retain the number of bytes returned.
retval = reader.GetBytes(0, startIndex, buffer, 0, bufferSize);
// Continue while there are bytes beyond the size of the buffer.
while (retval == bufferSize)
{
writer.Write(buffer);
writer.Flush();
// Reposition start index to end of last buffer and fill buffer.
startIndex += bufferSize;
retval = reader.GetBytes(0, startIndex, buffer, 0, bufferSize);
}
//output = reader.GetString(0);
}
reader.Close();
stream.Position = 0L;
StreamReader sr = new StreamReader(stream);
output = sr.ReadToEnd(); <---- Exception happens here
//output = new string(buffer);
Edit6: 要添加到這一點,當OOM發生異常我看到IIS工作進程(持有運行方法)差點撞到700MB。這是在IIS Express上運行的,而不是生產服務器上的完整IIS。這與它有什麼關係?另外當我調用Byte [] data = stream.ToArray()時,我也間歇性地獲取了OOM。我認爲我真正需要的是爲這個過程提供更多內存的方式,但我不知道在哪裏配置。
編輯7: 我剛剛在我的本地計算機上使用IIS Express將我的開發服務器更改爲內置的Visual Studio Web服務器。 OOM異常現在消失了。我真的認爲這是分配一塊連續的內存問題,無論出於何種原因,IIS Express都不會將其分叉。現在它運行良好,我將在運行常規IIS7的2008R2上發佈到我的完整服務器,以查看它是如何發生的。
您還應該包含完整的錯誤消息。 – 2013-02-27 22:50:31
正在返回的字符串有多大?換句話說,ReportData有多大? – 2013-02-27 22:50:57
顯示異常的完整堆棧跟蹤。 – 2013-02-27 22:58:13