.NET开发者常会忽略的几个错误
在運(yùn)用Visiol studio.NET 開(kāi)發(fā)Web 應(yīng)用程式中,開(kāi)發(fā)者常常會(huì)遇到一些問(wèn)題:如我開(kāi)發(fā)好的程式,在開(kāi)發(fā)環(huán)境下測(cè)試沒(méi)問(wèn)題,怎么一搬到應(yīng)用環(huán)境下,就會(huì)有問(wèn)題? 不是程式的無(wú)法運(yùn)行,就是程式的效率慢的同蝸牛在爬, 這種情況在.NET的新手中尤其常見(jiàn)。我不知道為什么,一些介紹.NET開(kāi)發(fā)的書(shū)本里引用的例子代碼,也對(duì)此問(wèn)題視而不見(jiàn),尤其讓我郁悶的是一些我喜歡的書(shū),如:<>,<>,這兩本都是清華大學(xué)出版社出版的, 有一本書(shū)更糟糕<>,我建議大家還是不要看了吧,免的浪費(fèi)時(shí)間和精力。
這篇文章不僅對(duì).NET開(kāi)發(fā)者的新手有幫助,同樣對(duì)哪些有經(jīng)驗(yàn),也帶來(lái)一些啟示和參考。
他們會(huì)遇到什么樣的問(wèn)題,我不妨總結(jié)給大家:
1. 數(shù)據(jù)庫(kù)連接超時(shí)
2. 創(chuàng)建的對(duì)象只管用,不管釋放
3. 調(diào)試(Debug)模式下編譯后,就用于應(yīng)用環(huán)境中了
4. 實(shí)際作業(yè)模式分享
上面的問(wèn)題就像毒瘤,積累到一定程度就爆發(fā),且影響深遠(yuǎn)。
一、數(shù)據(jù)庫(kù)連接超時(shí)篇
若要知道數(shù)據(jù)庫(kù)連接超時(shí)問(wèn)題,先看下面一段代碼:
[Sample-01]:
Public Shared Function getOEMPN(ByVal psPN As String, ByRef OEMPN As String) As BSResult
0001 Dim clsResult As New BSResult
0002 Try
0003 clsResult.ResultID = -1
0004 Dim dtResult As New DataTable
0005 Dim Sql As String = String.Empty
0006 Dim clsOraDb As New clsOraClienDb
0007 Dim strConn As String = ConfigurationManager.ConnectionStrings("ConnectionString").ConnectionString
0008 clsOraDb.Open(strConn) ‘這里Open后,后面看不到 clsOraDb.Close
0009 Sql = "SELECT SATBMMBRND.OEMPN FRUNO FROM SATBMMBRND WHERE SATBMMBRND.MATNO = :MATNO"
0010 Dim params() As OracleParameter = {New OracleParameter("MATNO", psPN)}
0011 If clsOraDb.FillDataTable(Sql, dtResult, params) = False Then
0012 Return clsResult
0013 End If
0014 If dtResult Is Nothing Then
0015 Return clsResult
0016 End If
0017 If dtResult.Rows.Count > 0 Then
0018 OEMPN = dtResult.Rows(0)("FRUNO").ToString()
0019 Else
0020 OEMPN = ""
0021 End If
0022 clsResult.ResultID = 1
0023 Return clsResult
0024 Catch ex As Exception
0025 clsResult.ResultID = -1
0026 Return clsResult
0027 End Try
End Function
對(duì)上述代碼行的部分解釋:
0006:引用數(shù)據(jù)庫(kù)連接的類;
0008:打開(kāi)數(shù)據(jù)庫(kù)連接;
然后,整個(gè)函數(shù)你再找不到關(guān)閉數(shù)據(jù)庫(kù)連接的動(dòng)作,是要等著操作系統(tǒng)來(lái)釋放嗎? 有人就說(shuō)啦,看起來(lái)好像沒(méi)有什么大不了的,這僅僅是一個(gè)函數(shù)而已; 數(shù)據(jù)庫(kù)打開(kāi)連接,未關(guān)閉不會(huì)影響到整個(gè)應(yīng)用程式; 果真是這樣嗎?
讓我們談?wù)剶?shù)據(jù)庫(kù)連接的問(wèn)題,在Oracle數(shù)據(jù)庫(kù)里,一般默認(rèn)的數(shù)據(jù)庫(kù)連接數(shù)最多也就100多來(lái)個(gè),不會(huì)超過(guò)200個(gè),即使你改變這個(gè)連接數(shù),但無(wú)論怎樣,它的連接數(shù)是有限的,不可能無(wú)限地供你消耗。
在Web這個(gè)程式里,它不僅不會(huì)自動(dòng)關(guān)閉數(shù)據(jù)庫(kù)連接,象這樣的函數(shù)還會(huì)每次調(diào)用,都會(huì)重新用掉一個(gè)數(shù)據(jù)庫(kù)連接; 如果象這樣的函數(shù)很多的話,你就等著一個(gè)錯(cuò)誤警告頁(yè)面彈出來(lái),如Database Connection Timeout….等訊息。
這還不算什么,更有甚者,盡然在循環(huán)語(yǔ)句里寫(xiě)下面的代碼如 :
[Sample-02]
Foreach(DataRow row in tabl.select(“”,”P(pán)roductID”)
……………
clsOraDb.Open(strConn)
………….
Next
有人還喜歡玩下面的語(yǔ)句:
[Sample-03]
Foreach(DataRow row in tabl.select(“”,”P(pán)roductID”)
Foreach(DataColumn col in tbl.columns)
……………
clsOraDb.Open(strConn)
Next
………….
Next
說(shuō)到這,有人就問(wèn)啦,我在開(kāi)發(fā)環(huán)境下測(cè)試一點(diǎn)問(wèn)題都沒(méi)有呀?是呀,你是沒(méi)有問(wèn)題,我想問(wèn)的是,你開(kāi)發(fā)環(huán)境的測(cè)試數(shù)據(jù)有幾筆?
現(xiàn)在,問(wèn)題已經(jīng)知道在哪里,怎么解決?
針對(duì)[sample-01]做如下處理,注意下面代碼:
Public Shared Function getOEMPN(ByVal psPN As String, ByRef OEMPN As String) As BSResult
0001 Dim clsResult As New BSResult
0002 Try
0003 clsResult.ResultID = -1
0004 Dim dtResult As New DataTable
0005 Dim Sql As String = String.Empty
0006 Dim clsOraDb As New clsOraClienDb
0007 Dim strConn As String = ConfigurationManager.ConnectionStrings("ConnectionString").ConnectionString
0008 clsOraDb.Open(strConn) 注釋:這里Open后,后面看不到 clsOraDb.Close
0009 Sql = "SELECT SATBMMBRND.OEMPN FRUNO FROM SATBMMBRND WHERE SATBMMBRND.MATNO = :MATNO"
0010 Dim params() As OracleParameter = {New OracleParameter("MATNO", psPN)}
0011 If clsOraDb.FillDataTable(Sql, dtResult, params) = False Then
0012 Return clsResult
0013 End If
0014 If dtResult Is Nothing Then
0015 Return clsResult
0016 End If
0017 If dtResult.Rows.Count > 0 Then
0018 OEMPN = dtResult.Rows(0)("FRUNO").ToString()
0019 Else
0020 OEMPN = ""
0021 End If
0022 clsResult.ResultID = 1
0088 clsOraDb.Close 注釋:后面看到 clsOraDb.Close
0023 Return clsResult
0024 Catch ex As Exception
0099 clsOraDb.Close 注釋:程序異常也看到 clsOraDb.Close
0025 clsResult.ResultID = -1
0026 Return clsResult
0028 Throw ex
0027 End Try
End Function
注意上面的兩句代碼:0088行和0099行。
在異常處理的時(shí)候,特別提醒兩點(diǎn):
一, 你的數(shù)據(jù)庫(kù)關(guān)閉的時(shí)候應(yīng)該是在代碼行0028前,而不是后;
二, 有人不習(xí)慣(或者一時(shí)疏忽)加上0088行的代碼;
針對(duì)[Sample-02]和[sample-03],把打開(kāi)數(shù)據(jù)庫(kù)連接寫(xiě)在所有的循環(huán)語(yǔ)句之前,如:
clsOraDb.Open(strConn)
Foreach(DataRow row in tabl.select(“”,”P(pán)roductID”)
……………
………….
Next
當(dāng)然還有另外一個(gè)做法,就是用Using語(yǔ)
二、對(duì)象只管創(chuàng)建應(yīng)用,不管釋放篇
我們繼續(xù)用[Sample-01]的代碼,我們現(xiàn)在看0004行的代碼:
0004 Dim dtResult As New DataTable
誰(shuí)會(huì)發(fā)現(xiàn)它被釋放,你不能,我也不能,從來(lái)沒(méi)有被釋放過(guò)。
“0004”行的代碼解釋是,要在內(nèi)存劃分一個(gè)空間給這個(gè)定義的對(duì)象dtresult; 系統(tǒng)要?jiǎng)澐侄啻蟮目臻g呢?呀,我沒(méi)有研究過(guò)(留給那些有心人吧,呵呵..)。但有一點(diǎn),要在內(nèi)存劃分一個(gè)空間,就是要占用內(nèi)存。那么內(nèi)存有多大呢,不是無(wú)限大吧,也是有限的,所有運(yùn)行上述代碼的最終結(jié)果是,系統(tǒng)的執(zhí)行效率越來(lái)越慢,有人就懷疑,我有內(nèi)存1到2G的,加上虛擬內(nèi)存就更大,我只能說(shuō)你的懷疑沒(méi)錯(cuò)??墒悄愕膽?yīng)用程序就用這么一只函數(shù)嗎?我想肯定不是,所以上百只函數(shù)的應(yīng)用執(zhí)行對(duì)內(nèi)存的消耗可想而知。如果是后臺(tái)自動(dòng)運(yùn)行的程序,及時(shí)是一個(gè)function,也會(huì)讓系統(tǒng)崩潰。這只是一個(gè)簡(jiǎn)單的例子,有更復(fù)雜的。像這樣的對(duì)象應(yīng)用還有:Dataset, Datatable,DataReader,DataAdapter,Datagrid..等。
那么怎么解決這些問(wèn)題呢:
2.1在Try catch 語(yǔ)句前定義好所用的對(duì)象, 如:
Dim dtResult As New DataTable
Dim DR as New DataReader
Dim DS as New Dataset
Try
..
Catch ex As Exception
Throw ex
Finally
End Try
2.2釋放的語(yǔ)句如下
Dim dtResult As New DataTable
Dim DR as New DataReader
Dim DS as New Dataset
Try
..
……………..
Catch ex As Exception
--釋放應(yīng)用的對(duì)象
Throw ex
Finally
--使用完后,釋放應(yīng)用的對(duì)象
dtResult.dispose --從內(nèi)存里清楚該對(duì)象
DR.dispose -從內(nèi)存里清楚該對(duì)象
DS.dispose -從內(nèi)存里清楚該對(duì)象
End Try
有人習(xí)慣寫(xiě)成下面這樣:
Dim dtResult As New DataTable
Dim DR as New DataReader
Dim DS as New Dataset
Try
..
‘使用完后,釋放應(yīng)用的對(duì)象
dtResult.dispose ‘從內(nèi)存里清楚該對(duì)象
DR.dispose ‘從內(nèi)存里清楚該對(duì)象
DS.dispose ‘從內(nèi)存里清楚該對(duì)象
Catch ex As Exception
‘釋放應(yīng)用的對(duì)象
Throw ex
Finally
End Try
這不是也釋放了嗎?我想問(wèn)的是,如果程序出現(xiàn)異常,它們會(huì)釋放嗎 ?我肯定得告訴大家,它們一定不能釋放,為了確保程序的穩(wěn)定運(yùn)行,我建議大家都來(lái)用Try Catch語(yǔ)句。
2.3 絕不建議在循環(huán)語(yǔ)句寫(xiě)如下的語(yǔ)句, 具體為什么,自己思考。
Foreach (DataRow row in tabl.select(“”,”P(pán)roductID”)
……………
Dim DS new Dataset 記住此乃寫(xiě)代碼之大忌;
Dim DT new Databable….
………….
Next
還有一種寫(xiě)法
Dim DS new Dataset
Dim DT new Databable…
Foreach (DataRow row in tabl.select(“”,”P(pán)roductID”)
DS=GetDatase
DT=GetDatatable……………
.
………….
Next
正確的寫(xiě)法是:
Dim DS new Dataset
Dim DT new Databable…
Try
Foreach (DataRow row in tabl.select(“”,”P(pán)roductID”)
DS=nothing ‘每次使用,都先把內(nèi)存空間釋放出來(lái)
DT=nothing ‘每次使用,都先把內(nèi)存空間釋放出來(lái)
DS=GetDatase
DT=GetDatatable……………
.
………….
Next
Catch ex As Exception
Throw ex
Finally
DS.dispose
DT.dispose
End Try
另外,提醒大家一點(diǎn),記得用 For Each 語(yǔ)句替代For i=0 to Rowcount-1,這樣的效率改善也是明顯的。
三、調(diào)試(Debug)模式下編譯就用于應(yīng)用環(huán)境中篇
接著,程式開(kāi)發(fā)好(也包括單元測(cè)試),然后編譯直接分發(fā)到應(yīng)用環(huán)境。
整個(gè)過(guò)程就結(jié)束了。誰(shuí)也不曾想,這里埋下了一個(gè)深深的地雷,據(jù)微軟的人講,這樣分發(fā)的程式到應(yīng)用環(huán)境,你有多少內(nèi)存恐怕都不夠。
所以微軟建議我們做如下的工作:
“請(qǐng)將Web.config中的debug及Trace均設(shè)為False。還有您的所有程式請(qǐng)確保compile為Release Mode
Application set up for debugging
One reason for high memory that we see here in Support a lot is when you have debugging, tracing, or both enabled for your application.
While you are developing your application, this is a necessity.By default, when you create your application in Visual Studio .NET, you will see the following attribute set in your Web.config file:
and/or
Also, when you do a final build of your application, make sure that you do this in "Release" mode, not "Debug" mode. ”
如果不這樣做會(huì)有什么事情發(fā)生? 我分享一個(gè)同事的感受給大家:
“已經(jīng)是很強(qiáng)悍的的DB和AP服務(wù)器(全部是刀片式服務(wù)器)了,可是問(wèn)題一而再再而三地發(fā)生,那種感覺(jué)真的很無(wú)助很凄涼啊~”
后來(lái)的結(jié)果發(fā)現(xiàn)是內(nèi)存使用率超高,到一定限度的時(shí)候,就會(huì)反應(yīng)變慢,這個(gè)時(shí)候只要重啟IIS就可以好一段時(shí)間。后來(lái)分析IIS用到實(shí)體加虛擬的內(nèi)存超過(guò)2G就會(huì)爆掉。
這就是原因,你想遇到嗎?那就不妨試試。
四、實(shí)際操作分享篇
上面三個(gè)環(huán)節(jié),任何一個(gè)發(fā)生問(wèn)題,都會(huì)影響到系統(tǒng)的效率。我分享我們實(shí)際的作業(yè)的過(guò)程發(fā)生的一些情況,及怎么解決這些問(wèn)題。
4.1 內(nèi)存使用達(dá)到峰值,導(dǎo)致程序無(wú)法繼續(xù)運(yùn)行。
有個(gè)同事分享了他們的經(jīng)驗(yàn)如下(原話分享):
我們有一些程式是server跑的Job,并有越來(lái)越多之勢(shì)。而大家在寫(xiě)程式的時(shí)候可能比較少考慮到耗內(nèi)存這個(gè)問(wèn)題。
下面的例子也許會(huì)給我們一點(diǎn)啟示。
下面也是原話
Pls help to check the Run In Rack Job program . It will no response after running two or three days . the AP server Memory usage will over 2.5G . after we close the the program , Memory will decrease to 1.5 .
大致意思是:在服務(wù)器端(也叫后臺(tái))自動(dòng)跑的一只程式,運(yùn)行了兩三天后,停止運(yùn)行了。檢查Server的內(nèi)存使用率時(shí),發(fā)現(xiàn)超過(guò)了2.5G; 在關(guān)掉了這只程式后,它就降到了1.5G…
4.2 進(jìn)程請(qǐng)求過(guò)多,導(dǎo)致CPU無(wú)法及時(shí)處理,程序效率反應(yīng)較慢。
下面都是同事的原話:
“年后產(chǎn)量逐漸增加,新的問(wèn)題又出現(xiàn)了。從Server Performance上分析,和上次Memory過(guò)高不同的是CPU使用率過(guò)高。
每當(dāng)CPU過(guò)高的時(shí)候,產(chǎn)線會(huì)大面積的反應(yīng)說(shuō)慢(這點(diǎn)和連接到哪臺(tái)AP有關(guān)系)。
每次慢的時(shí)候,我們就找到CPU過(guò)高的那臺(tái)AP,recycle IIS的application pool后就OK了。
于是我么再次找到Bon幫忙分析(結(jié)論:微軟結(jié)案報(bào)告 20090226V1 - SRT090119833891 Web service can't serve IISReset can fix.msg)。并給出了開(kāi)發(fā)程序時(shí)的一些建議。
結(jié)論大致是說(shuō),沒(méi)有進(jìn)程占用了特別高的CPU,也沒(méi)有進(jìn)程占用CPU時(shí)間過(guò)長(zhǎng)。只是對(duì)DB的請(qǐng)求的進(jìn)程過(guò)多(比較吻合3廠的實(shí)際狀況—附件多,刷的快),加起來(lái)就整體過(guò)高。
還發(fā)現(xiàn)了很多DLL是built in debug mode,這些DLL占用了過(guò)多的memory資源。
后來(lái)根據(jù)Bon的建議,我們修改了IIS application pool的設(shè)定如下,解決過(guò)多請(qǐng)求不能及時(shí)處理,而造成CPU過(guò)高的問(wèn)題?!?
這里有一些問(wèn)答關(guān)于應(yīng)用連接池(Application Pool)的設(shè)定,對(duì)理解這樣的設(shè)置有一定的幫助:
1. Is one application pool’s maximum memory usage 1.5G?
A&: Each application pool is a w3wp.exe. w3wp.exe is a process. Every process has 2 G User mode virtual address, so the maximum memory usage for application pool is 2G. However, you can’t make sure that there is no memory fragment issue. Therefore, Out of memory always occur after 1.5 G according to our experience.
2. Is each application pool independent on memory usage?
A&: Different application pools are different w3wp.exe, so each application pool’s maximum memory usage is 2G.
3. Can setup maximum CPU usage on each application pool?
A&: You can monitor it, but you can’t setup it.
寫(xiě)完這篇文章的時(shí)候,我們另一項(xiàng)目組的開(kāi)發(fā)者,也遇到了同樣的問(wèn)題,他們開(kāi)發(fā)好的一只程式,用戶反映很慢。我跑過(guò)去一看代碼,完了,所有我描述的問(wèn)題,他們都無(wú)一避免地出現(xiàn)啦,那就趕緊告訴他們,還等什么?
轉(zhuǎn)載于:https://www.cnblogs.com/huyong/archive/2009/07/16/2685626.html
總結(jié)
以上是生活随笔為你收集整理的.NET开发者常会忽略的几个错误的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: sql server 关联left jo
- 下一篇: asp.net ajax控件工具集 Au