青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品

思勤無邪

上學(xué)時,因我年齡最小,個頭也最小,上課時,就像大猩猩堆里的猴一般。如今,這猴偶爾也把最近的一些情況寫在這里。

   :: 首頁 :: 聯(lián)系 :: 聚合  :: 管理
  132 Posts :: 1 Stories :: 178 Comments :: 0 Trackbacks

公告

     吾日常三省吾身,曰思、曰勤、曰無邪。

積分與排名

  • 積分 - 186424
  • 排名 - 140

最新隨筆

最新評論

閱讀排行榜

評論排行榜

    從2004年底開始接觸C#到現(xiàn)在也有2年多的時間了,因為有C++方面的基礎(chǔ),對于C#,我習(xí)慣于與C++對比。現(xiàn)在總結(jié)一些.NET方面的性能優(yōu)化方面的經(jīng)驗,算是對這兩年多的.NET工作經(jīng)歷的總結(jié)。
    由于使用C#時間不長,歡迎各高手補充。
    標(biāo)有 表示特別重要,會嚴(yán)重影響性能,程序中不應(yīng)出現(xiàn)的情況。

    1.  C#語言方面

    1.1 垃圾回收
    垃圾回收解放了手工管理對象的工作,提高了程序的健壯性,但副作用就是程序代碼可能對于對象創(chuàng)建變得隨意。
    1.1.1 避免不必要的對象創(chuàng)建
    由于垃圾回收的代價較高,所以C#程序開發(fā)要遵循的一個基本原則就是避免不必要的對象創(chuàng)建。以下列舉一些常見的情形。
    1.1.1.1 避免循環(huán)創(chuàng)建對象
    如果對象并不會隨每次循環(huán)而改變狀態(tài),那么在循環(huán)中反復(fù)創(chuàng)建對象將帶來性能損耗。高效的做法是將對象提到循環(huán)外面創(chuàng)建。
    1.1.1.2 在需要邏輯分支中創(chuàng)建對象
    如果對象只在某些邏輯分支中才被用到,那么應(yīng)只在該邏輯分支中創(chuàng)建對象。
    1.1.1.3 使用常量避免創(chuàng)建對象
    程序中不應(yīng)出現(xiàn)如 new Decimal(0) 之類的代碼,這會導(dǎo)致小對象頻繁創(chuàng)建及回收,正確的做法是使用Decimal.Zero常量。我們有設(shè)計自己的類時,也可以學(xué)習(xí)這個設(shè)計手法,應(yīng)用到類似的場景中。
    1.1.1.4 使用StringBuilder做字符串連接

    1.1.2 不要使用空析構(gòu)函數(shù)
    如果類包含析構(gòu)函數(shù),由創(chuàng)建對象時會在 Finalize 隊列中添加對象的引用,以保證當(dāng)對象無法可達時,仍然可以調(diào)用到 Finalize 方法。垃圾回收器在運行期間,會啟動一個低優(yōu)先級的線程處理該隊列。相比之下,沒有析構(gòu)函數(shù)的對象就沒有這些消耗。如果析構(gòu)函數(shù)為空,這個消耗就毫無意義,只會導(dǎo)致性能降低!因此,不要使用空的析構(gòu)函數(shù)。
    在實際情況中,許多曾在析構(gòu)函數(shù)中包含處理代碼,但后來因為種種原因被注釋掉或者刪除掉了,只留下一個空殼,此時應(yīng)注意把析構(gòu)函數(shù)本身注釋掉或刪除掉。
    1.1.3 實現(xiàn) IDisposable 接口
    垃圾回收事實上只支持托管內(nèi)在的回收,對于其他的非托管資源,例如 Window GDI 句柄或數(shù)據(jù)庫連接,在析構(gòu)函數(shù)中釋放這些資源有很大問題。原因是垃圾回收依賴于內(nèi)在緊張的情況,雖然數(shù)據(jù)庫連接可能已瀕臨耗盡,但如果內(nèi)存還很充足的話,垃圾回收是不會運行的。
    C#的 IDisposable 接口是一種顯式釋放資源的機制。通過提供 using 語句,還簡化了使用方式(編譯器自動生成 try ... finally 塊,并在 finally 塊中調(diào)用 Dispose 方法)。對于申請非托管資源對象,應(yīng)為其實現(xiàn) IDisposable 接口,以保證資源一旦超出 using 語句范圍,即得到及時釋放。這對于構(gòu)造健壯且性能優(yōu)良的程序非常有意義!
    為防止對象的 Dispose 方法不被調(diào)用的情況發(fā)生,一般還要提供析構(gòu)函數(shù),兩者調(diào)用一個處理資源釋放的公共方法。同時,Dispose 方法應(yīng)調(diào)用 System.GC.SuppressFinalize(this),告訴垃圾回收器無需再處理 Finalize 方法了。

    1.2 String 操作
    1.2.1 使用 StringBuilder 做字符串連接
    String 是不變類,使用 + 操作連接字符串將會導(dǎo)致創(chuàng)建一個新的字符串。如果字符串連接次數(shù)不是固定的,例如在一個循環(huán)中,則應(yīng)該使用 StringBuilder 類來做字符串連接工作。因為 StringBuilder 內(nèi)部有一個 StringBuffer ,連接操作不會每次分配新的字符串空間。只有當(dāng)連接后的字符串超出 Buffer 大小時,才會申請新的 Buffer 空間。典型代碼如下:

StringBuilder sb  =   new  StringBuilder( 256 );
for  ( int  i = 0 ; i <  Results.Count; i ++ )
{
  sb.Append (Results[i]);
}

    如果連接次數(shù)是固定的并且只有幾次,此時應(yīng)該直接用 + 號連接,保持程序簡潔易讀。實際上,編譯器已經(jīng)做了優(yōu)化,會依據(jù)加號次數(shù)調(diào)用不同參數(shù)個數(shù)的 String.Concat 方法。例如:
    String str = str1 + str2 + str3 + str4;
    會被編譯為 String.Concat(str1, str2, str3, str4)。該方法內(nèi)部會計算總的 String 長度,僅分配一次,并不會如通常想象的那樣分配三次。作為一個經(jīng)驗值,當(dāng)字符串連接操作達到 10 次以上時,則應(yīng)該使用 StringBuilder。
    這里有一個細(xì)節(jié)應(yīng)注意:StringBuilder 內(nèi)部 Buffer 的缺省值為 16 ,這個值實在太小。按 StringBuilder 的使用場景,Buffer 肯定得重新分配。經(jīng)驗值一般用 256 作為 Buffer 的初值。當(dāng)然,如果能計算出最終生成字符串長度的話,則應(yīng)該按這個值來設(shè)定 Buffer 的初值。使用 new StringBuilder(256) 就將 Buffer 的初始長度設(shè)為了256。
    1.2.2 避免不必要的調(diào)用 ToUpper 或 ToLower 方法
    String是不變類,調(diào)用ToUpper或ToLower方法都會導(dǎo)致創(chuàng)建一個新的字符串。如果被頻繁調(diào)用,將導(dǎo)致頻繁創(chuàng)建字符串對象。這違背了前面講到的“避免頻繁創(chuàng)建對象”這一基本原則。
    例如,bool.Parse方法本身已經(jīng)是忽略大小寫的,調(diào)用時不要調(diào)用ToLower方法。
    另一個非常普遍的場景是字符串比較。高效的做法是使用 Compare 方法,這個方法可以做大小寫忽略的比較,并且不會創(chuàng)建新字符串。
    還有一種情況是使用 HashTable 的時候,有時候無法保證傳遞 key 的大小寫是否符合預(yù)期,往往會把 key 強制轉(zhuǎn)換到大寫或小寫方法。實際上 HashTable 有不同的構(gòu)造形式,完全支持采用忽略大小寫的 key: new HashTable(StringComparer.OrdinalIgnoreCase)。
    1.2.3 最快的空串比較方法
    將String對象的Length屬性與0比較是最快的方法:if (str.Length == 0)
    其次是與String.Empty常量或空串比較:if (str == String.Empty)或if (str == "")
    注:C#在編譯時會將程序集中聲明的所有字符串常量放到保留池中(intern pool),相同常量不會重復(fù)分配。

    1.3 多線程

    1.3.1 線程同步
    線程同步是編寫多線程程序需要首先考慮問題。C#為同步提供了 Monitor、Mutex、AutoResetEvent 和 ManualResetEvent 對象來分別包裝 Win32 的臨界區(qū)、互斥對象和事件對象這幾種基礎(chǔ)的同步機制。C#還提供了一個lock語句,方便使用,編譯器會自動生成適當(dāng)?shù)?Monitor.Enter 和 Monitor.Exit 調(diào)用。
    1.3.1.1 同步粒度
    同步粒度可以是整個方法,也可以是方法中某一段代碼。為方法指定 MethodImplOptions.Synchronized 屬性將標(biāo)記對整個方法同步。例如:

[MethodImpl(MethodImplOptions.Synchronized)]
public   static  SerialManager GetInstance()
{
    
if  (instance  ==   null )
    
{
        instance 
=   new  SerialManager();
    }

    
return  instance;
}

    通常情況下,應(yīng)減小同步的范圍,使系統(tǒng)獲得更好的性能。簡單將整個方法標(biāo)記為同步不是一個好主意,除非能確定方法中的每個代碼都需要受同步保護。
    1.3.1.2 同步策略
    使用 lock 進行同步,同步對象可以選擇 Type、this 或為同步目的專門構(gòu)造的成員變量。
    避免鎖定Type★ 
    鎖定Type對象會影響同一進程中所有AppDomain該類型的所有實例,這不僅可能導(dǎo)致嚴(yán)重的性能問題,還可能導(dǎo)致一些無法預(yù)期的行為。這是一個很不好的習(xí)慣。即便對于一個只包含static方法的類型,也應(yīng)額外構(gòu)造一個static的成員變量,讓此成員變量作為鎖定對象。
    避免鎖定 this
    鎖定 this 會影響該實例的所有方法。假設(shè)對象 obj 有 A 和 B 兩個方法,其中 A 方法使用 lock(this) 對方法中的某段代碼設(shè)置同步保護。現(xiàn)在,因為某種原因,B 方法也開始使用 lock(this) 來設(shè)置同步保護了,并且可能為了完全不同的目的。這樣,A 方法就被干擾了,其行為可能無法預(yù)知。所以,作為一種良好的習(xí)慣,建議避免使用 lock(this) 這種方式。
    使用為同步目的專門構(gòu)造的成員變量
    這是推薦的做法。方式就是 new 一個 object 對象, 該對象僅僅用于同步目的。
    如果有多個方法都需要同步,并且有不同的目的,那么就可以為些分別建立幾個同步成員變量。

    1.3.1.4 集合同步
    C#為各種集合類型提供了兩種方便的同步機制:Synchronized 包裝器和 SyncRoot 屬性。

//  Creates and initializes a new ArrayList
ArrayList myAL  =   new  ArrayList();
myAL.Add(
" The " );
myAL.Add(
" quick " );
myAL.Add(
" brown " );
myAL.Add(
" fox " );

//  Creates a synchronized wrapper around the ArrayList
ArrayList mySyncdAL  =  ArrayList.Synchronized(myAL);

    調(diào)用 Synchronized 方法會返回一個可保證所有操作都是線程安全的相同集合對象。考慮 mySyncdAL[0] = mySyncdAL[0] + "test" 這一語句,讀和寫一共要用到兩個鎖。一般講,效率不高。推薦使用 SyncRoot 屬性,可以做比較精細(xì)的控制。

    1.3.2 使用 ThreadStatic 替代 NameDataSlot
    存取 NameDataSlot 的 Thread.GetData 和 Thread.SetData 方法需要線程同步,涉及兩個鎖:一個是 LocalDataStore.SetData 方法需要在 AppDomain 一級加鎖,另一個是 ThreadNative.GetDomainLocalStore 方法需要在 Process 一級加鎖。如果一些底層的基礎(chǔ)服務(wù)使用了 NameDataSlot,將導(dǎo)致系統(tǒng)出現(xiàn)嚴(yán)重的伸縮性問題。
    規(guī)避這個問題的方法是使用 ThreadStatic 變量。示例如下:

public   sealed   class  InvokeContext
{
    [ThreadStatic]
    
private   static  InvokeContext current;
    
private  Hashtable maps  =   new  Hashtable();
}



    1.3.3 多線程編程技巧
    1.3.3.1 使用 Double Check 技術(shù)創(chuàng)建對象

internal  IDictionary KeyTable
{
    
get
    
{
        
if  ( this ._keyTable  ==   null )
        
{
            
lock  ( base ._lock)
            
{
                
if  ( this ._keyTable  ==   null )
                
{
                    
this ._keyTable  =   new  Hashtable();
                }

            }

        }

        
return   this ._keyTable;
    }

}

創(chuàng)建單例對象是很常見的一種編程情況。一般在 lock 語句后就會直接創(chuàng)建對象了,但這不夠安全。因為在 lock 鎖定對象之前,可能已經(jīng)有多個線程進入到了第一個 if 語句中。如果不加第二個 if 語句,則單例對象會被重復(fù)創(chuàng)建,新的實例替代掉舊的實例。如果單例對象中已有數(shù)據(jù)不允許被破壞或者別的什么原因,則應(yīng)考慮使用 Double Check 技術(shù)。

    1.4 類型系統(tǒng)
    1.4.1 避免無意義的變量初始化動作
    CLR保證所有對象在訪問前已初始化,其做法是將分配的內(nèi)存清零。因此,不需要將變量重新初始化為0、false或null。
    需要注意的是:方法中的局部變量不是從堆而是從棧上分配,所以C#不會做清零工作。如果使用了未賦值的局部變量,編譯期間即會報警。不要因為有這個印象而對所有類的成員變量也做賦值動作,兩者的機理完全不同!
    1.4.2 ValueType 和 ReferenceType

    1.4.2.1 以引用方式傳遞值類型參數(shù)
    值類型從調(diào)用棧分配,引用類型從托管堆分配。當(dāng)值類型用作方法參數(shù)時,默認(rèn)會進行參數(shù)值復(fù)制,這抵消了值類型分配效率上的優(yōu)勢。作為一項基本技巧,以引用方式傳遞值類型參數(shù)可以提高性能。
    1.4.2.2 為 ValueType 提供 Equals 方法
    .net 默認(rèn)實現(xiàn)的 ValueType.Equals 方法使用了反射技術(shù),依靠反射來獲得所有成員變量值做比較,這個效率極低。如果我們編寫的值對象其 Equals 方法要被用到(例如將值對象放到 HashTable 中),那么就應(yīng)該重載 Equals 方法。

public   struct  Rectangle
{
    
public   double  Length;
    
public   double  Breadth;
    
public   override   bool  Equals ( object  ob)
    
{
        
if  (ob  is  Rectangle)
            
return  Equels ((Rectangle)ob))
        
else
            
return   false ;
    }

    
private   bool  Equals (Rectangle rect)
    
{
        
return   this .Length  ==  rect.Length  &&   this .Breadth  ==  rect.Breach;
    }

}


    1.4.2.3 避免裝箱和拆箱
    C#可以在值類型和引用類型之間自動轉(zhuǎn)換,方法是裝箱和拆箱。裝箱需要從堆上分配對象并拷貝值,有一定性能消耗。如果這一過程發(fā)生在循環(huán)中或是作為底層方法被頻繁調(diào)用,則應(yīng)該警惕累計的效應(yīng)。
    一種經(jīng)常的情形出現(xiàn)在使用集合類型時。例如:

ArrayList al  =   new  ArrayList();
for  ( int  i  =   0 ; i  <   1000 ; i ++ )
{
    al.Add(i);    
//  Implicitly boxed because Add() takes an object
}

int  f  =  ( int )al[ 0 ];     //  The element is unboxed


    1.5 異常處理
    異常也是現(xiàn)代語言的典型特征。與傳統(tǒng)檢查錯誤碼的方式相比,異常是強制性的(不依賴于是否忘記了編寫檢查錯誤碼的代碼)、強類型的、并帶有豐富的異常信息(例如調(diào)用棧)。
    1.5.1 不要吃掉異常
    關(guān)于異常處理的最重要原則就是:不要吃掉異常。這個問題與性能無關(guān),但對于編寫健壯和易于排錯的程序非常重要。這個原則換一種說法,就是不要捕獲那些你不能處理的異常。
    吃掉異常是極不好的習(xí)慣,因為你消除了解決問題的線索。一旦出現(xiàn)錯誤,定位問題將非常困難。除了這種完全吃掉異常的方式外,只將異常信息寫入日志文件但并不做更多處理的做法也同樣不妥。
    1.5.2 不要吃掉異常信息
    有些代碼雖然拋出了異常,但卻把異常信息吃掉了。
    為異常披露詳盡的信息是程序員的職責(zé)所在。如果不能在保留原始異常信息含義的前提下附加更豐富和更人性化的內(nèi)容,那么讓原始的異常信息直接展示也要強得多。千萬不要吃掉異常。
    1.5.3 避免不必要的拋出異常
    拋出異常和捕獲異常屬于消耗比較大的操作,在可能的情況下,應(yīng)通過完善程序邏輯避免拋出不必要不必要的異常。與此相關(guān)的一個傾向是利用異常來控制處理邏輯。盡管對于極少數(shù)的情況,這可能獲得更為優(yōu)雅的解決方案,但通常而言應(yīng)該避免。
    1.5.4 避免不必要的重新拋出異常
    如果是為了包裝異常的目的(即加入更多信息后包裝成新異常),那么是合理的。但是有不少代碼,捕獲異常沒有做任何處理就再次拋出,這將無謂地增加一次捕獲異常和拋出異常的消耗,對性能有傷害。

    1.6 反射
    反射是一項很基礎(chǔ)的技術(shù),它將編譯期間的靜態(tài)綁定轉(zhuǎn)換為延遲到運行期間的動態(tài)綁定。在很多場景下(特別是類框架的設(shè)計),可以獲得靈活易于擴展的架構(gòu)。但帶來的問題是與靜態(tài)綁定相比,動態(tài)綁定會對性能造成較大的傷害。
    1.6.1 反射分類
     type comparison :類型判斷,主要包括 is 和 typeof 兩個操作符及對象實例上的 GetType 調(diào)用。這是最輕型的消耗,可以無需考慮優(yōu)化問題。注意 typeof 運算符比對象實例上的 GetType 方法要快,只要可能則優(yōu)先使用 typeof 運算符。
    member enumeration : 成員枚舉,用于訪問反射相關(guān)的元數(shù)據(jù)信息,例如Assembly.GetModule、Module.GetType、Type對象上的IsInterface、IsPublic、GetMethod、GetMethods、GetProperty、GetProperties、GetConstructor調(diào)用等。盡管元數(shù)據(jù)都會被CLR緩存,但部分方法的調(diào)用消耗仍非常大,不過這類方法調(diào)用頻度不會很高,所以總體看性能損失程度中等。
    member invocation:成員調(diào)用,包括動態(tài)創(chuàng)建對象及動態(tài)調(diào)用對象方法,主要有Activator.CreateInstance、Type.InvokeMember等。
    1.6.2 動態(tài)創(chuàng)建對象
    C#主要支持 5 種動態(tài)創(chuàng)建對象的方式:
    1. Type.InvokeMember
    2. ContructorInfo.Invoke
    3. Activator.CreateInstance(Type)
    4. Activator.CreateInstance(assemblyName, typeName)
    5. Assembly.CreateInstance(typeName)
    最快的是方式 3 ,與 Direct Create 的差異在一個數(shù)量級之內(nèi),約慢 7 倍的水平。其他方式,至少在 40 倍以上,最慢的是方式 4 ,要慢三個數(shù)量級。
    1.6.3 動態(tài)方法調(diào)用
    方法調(diào)用分為編譯期的早期綁定和運行期的動態(tài)綁定兩種,稱為Early-Bound Invocation和Late-Bound Invocation。Early-Bound Invocation可細(xì)分為Direct-call、Interface-call和Delegate-call。Late-Bound Invocation主要有Type.InvokeMember和MethodBase.Invoke,還可以通過使用LCG(Lightweight Code Generation)技術(shù)生成IL代碼來實現(xiàn)動態(tài)調(diào)用。
    從測試結(jié)果看,相比Direct Call,Type.InvokeMember要接近慢三個數(shù)量級;MethodBase.Invoke雖然比Type.InvokeMember要快三倍,但比Direct Call仍慢270倍左右。可見動態(tài)方法調(diào)用的性能是非常低下的。我們的建議是:除非要滿足特定的需求,否則不要使用!
    1.6.4 推薦的使用原則
    模式
    1. 如果可能,則避免使用反射和動態(tài)綁定
    2. 使用接口調(diào)用方式將動態(tài)綁定改造為早期綁定
    3. 使用Activator.CreateInstance(Type)方式動態(tài)創(chuàng)建對象
    4. 使用typeof操作符代替GetType調(diào)用
    反模式
    1. 在已獲得Type的情況下,卻使用Assembly.CreateInstance(type.FullName)

    1.7 基本代碼技巧
    這里描述一些應(yīng)用場景下,可以提高性能的基本代碼技巧。對處于關(guān)鍵路徑的代碼,進行這類的優(yōu)化還是很有意義的。普通代碼可以不做要求,但養(yǎng)成一種好的習(xí)慣也是有意義的。
    1.7.1 循環(huán)寫法
    可以把循環(huán)的判斷條件用局部變量記錄下來。局部變量往往被編譯器優(yōu)化為直接使用寄存器,相對于普通從堆或棧中分配的變量速度快。如果訪問的是復(fù)雜計算屬性的話,提升效果將更明顯。for (int i = 0, j = collection.GetIndexOf(item); i < j; i++)
    需要說明的是:這種寫法對于CLR集合類的Count屬性沒有意義,原因是編譯器已經(jīng)按這種方式做了特別的優(yōu)化。
    1.7.2 拼裝字符串
    拼裝好之后再刪除是很低效的寫法。有些方法其循環(huán)長度在大部分情況下為1,這種寫法的低效就更為明顯了:

public   static   string  ToString(MetadataKey entityKey)
{
    
string  str  =   "" ;
    
object [] vals  =  entityKey.values;
    
for  ( int  i  =   0 ; i  <  vals.Length; i ++ )
    
{
        str 
+=   " , "   +  vals[i].ToString();
    }

    
return  str  ==   ""   ?   ""  : str.Remove( 0 1 );
}

    推薦下面的寫法:

if  (str.Length  ==   0 )
    str 
=  vals[i].ToString();
else
    str 
+=   " , "   +  vals[i].ToString();

    其實這種寫法非常自然,而且效率很高,完全不需要用個Remove方法繞來繞去。
    1.7.3 避免兩次檢索集合元素
    獲取集合元素時,有時需要檢查元素是否存在。通常的做法是先調(diào)用ContainsKey(或Contains)方法,然后再獲取集合元素。這種寫法非常符合邏輯。 
但如果考慮效率,可以先直接獲取對象,然后判斷對象是否為null來確定元素是否存在。對于Hashtable,這可以節(jié)省一次GetHashCode調(diào)用和n次Equals比較。

如下面的示例:

public  IData GetItemByID(Guid id)
{
    IData data1 
=   null ;
    
if  ( this .idTable.ContainsKey(id.ToString())
    
{
        data1 
=   this .idTable[id.ToString()]  as  IData;
    }

    
return  data1;
}

其實完全可用一行代碼完成:return this.idTable[id] as IData;
    1.7.4 避免兩次類型轉(zhuǎn)換
    考慮如下示例,其中包含了兩處類型轉(zhuǎn)換: 

if  (obj  is  SomeType)
{
    SomeType st 
=  (SomeType)obj;
    st.SomeTypeMethod();
}

    效率更高的做法如下:

SomeType st  =  obj  as  SomeType;
if  (st  !=   null )
{
    st.SomeTypeMethod();
}


    1.8 Hashtable
    Hashtable是一種使用非常頻繁的基礎(chǔ)集合類型。需要理解影響Hashtable的效率有兩個因素:一是散列碼(GetHashCode方法),二是等值比較(Equals方法)。Hashtable首先使用鍵的散列碼將對象分布到不同的存儲桶中,隨后在該特定的存儲桶中使用鍵的Equals方法進行查找。
良好的散列碼是第一位的因素,最理想的情況是每個不同的鍵都有不同的散列碼。Equals方法也很重要,因為散列只需要做一次,而存儲桶中查找鍵可能需要做多次。從實際經(jīng)驗看,使用Hashtable時,Equals方法的消耗一般會占到一半以上。

    System.Object類提供了默認(rèn)的GetHashCode實現(xiàn),使用對象在內(nèi)存中的地址作為散列碼。我們遇到過一個用Hashtable來緩存對象的例子,每次根據(jù)傳遞的OQL表達式構(gòu)造出一個ExpressionList對象,再調(diào)用QueryCompiler的方法編譯得到CompiledQuery對象。以ExpressionList對象和CompiledQuery對象作為鍵值對存儲到Hashtable中。ExpressionList對象沒有重載GetHashCode實現(xiàn),其超類ArrayList也沒有,這樣最后用的就是System.Object類的GetHashCode實現(xiàn)。由于ExpressionList對象會每次構(gòu)造,因此它的HashCode每次都不同,所以這個CompiledQueryCache根本就沒有起到預(yù)想的作用。這個小小的疏漏帶來了重大的性能問題,由于解析OQL表達式頻繁發(fā)生,導(dǎo)致CompiledQueryCache不斷增長,造成服務(wù)器內(nèi)存泄漏!解決這個問題的最簡單方法就是提供一個常量實現(xiàn),例如讓散列碼為常量0。雖然這會導(dǎo)致所有對象匯聚到同一個存儲桶中,效率不高,但至少可以解決掉內(nèi)存泄漏問題。當(dāng)然,最終還是會實現(xiàn)一個高效的GetHashCode方法的。
    以上介紹這些Hashtable機理,主要是希望大家理解:如果使用Hashtable,你應(yīng)該檢查一下對象是否提供了適當(dāng)?shù)腉etHashCode和Equals方法實現(xiàn)。否則,有可能出現(xiàn)效率不高或者與預(yù)期行為不符的情況。














posted on 2007-03-24 17:06 思勤無邪 閱讀(2781) 評論(0)  編輯 收藏 引用 所屬分類: .NET
青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品
  • <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>
            在线观看国产一区二区| 欧美国产精品中文字幕| 亚洲精品永久免费精品| 亚洲在线中文字幕| 欧美国产日韩a欧美在线观看| 亚洲神马久久| 欧美日韩精品一区二区| 在线亚洲观看| 亚洲欧洲精品一区二区三区波多野1战4 | 国产专区一区| 久久免费精品日本久久中文字幕| 亚洲一区不卡| 国产精品视频一区二区高潮| 亚洲欧美国产高清va在线播| 亚洲欧美亚洲| 亚洲激情不卡| 亚洲国产岛国毛片在线| 欧美日韩综合在线免费观看| 一本久久综合亚洲鲁鲁| 一区二区三区精品视频在线观看| 国产精品永久| 最新国产成人在线观看| 国产欧美日本在线| 亚洲国产精品福利| 国产精品久久国产三级国电话系列 | 久久精品国产亚洲一区二区三区 | 欧美在线不卡视频| 亚洲欧美日韩一区在线| 国内精品99| 亚洲视频香蕉人妖| 1024日韩| 翔田千里一区二区| 国产精品美女诱惑| 欧美激情精品久久久久久变态| 国产精品亚发布| 亚洲乱码日产精品bd| 亚洲国产色一区| 久久久久久久一区二区| 久久精品国产2020观看福利| 国产精品美女久久久浪潮软件| 日韩午夜剧场| 亚洲永久免费视频| 欧美高清不卡| 亚洲一区三区视频在线观看| 久久久国产精品一区| 亚洲欧洲精品一区二区三区波多野1战4 | 久久―日本道色综合久久| 欧美肥婆bbw| 午夜精品一区二区在线观看| 国产亚洲在线| 欧美午夜精品理论片a级按摩| 亚洲主播在线| 亚洲日本成人| 久久久欧美精品sm网站| 亚洲人午夜精品免费| 国产日韩欧美麻豆| 欧美精品久久久久久| 久久av在线看| 一区二区91| 国产一区二区三区在线观看视频 | 久久成人一区| 99国产精品99久久久久久粉嫩| 久久久噜噜噜| 欧美中在线观看| 亚洲一区二区三区高清不卡| 亚洲伦理在线免费看| 亚洲福利在线视频| 狠狠干狠狠久久| 国产日韩在线一区二区三区| 欧美色偷偷大香| 欧美三级日本三级少妇99| 免费成人高清| 久久一区免费| 欧美大片一区二区三区| 快she精品国产999| 久久躁日日躁aaaaxxxx| 久久香蕉国产线看观看av| 久久精品综合一区| 欧美制服丝袜| 久久蜜桃资源一区二区老牛| 欧美一区二区三区电影在线观看| 欧美一级视频| 久久久久一本一区二区青青蜜月| 久久精品国产91精品亚洲| 午夜精品国产| 免费成人你懂的| 国产精品久久中文| 狠色狠色综合久久| 亚洲婷婷综合久久一本伊一区| 国产精品99久久久久久久女警 | 亚洲欧美日韩综合| 欧美在线高清视频| 猛干欧美女孩| 日韩亚洲欧美在线观看| 亚洲午夜免费视频| 久久精品人人| 国产精品午夜国产小视频| 国产自产2019最新不卡| 日韩一级免费| 蜜桃精品久久久久久久免费影院| 亚洲国产精彩中文乱码av在线播放| 亚洲裸体在线观看| 老司机免费视频一区二区三区| 欧美性理论片在线观看片免费| 在线观看精品视频| 欧美专区18| 夜夜夜精品看看| 99精品99久久久久久宅男| 欧美一进一出视频| 日韩视频在线免费观看| 久久国产日韩| 国产精品v日韩精品| 欧美电影专区| 亚洲国内欧美| 欧美国产日韩一区二区三区| 黄色欧美日韩| 久久青草久久| 在线观看国产精品网站| 久久麻豆一区二区| 久久久国产精品亚洲一区| 亚洲国产免费看| 欧美精品一区二| 日韩亚洲国产欧美| 久久成年人视频| 日韩亚洲欧美成人一区| 亚洲电影成人| 欧美日韩一区二区在线| 一区二区电影免费在线观看| 在线视频日韩精品| 国产日韩精品电影| 老牛影视一区二区三区| 免费成人你懂的| 亚洲在线观看| 免费日韩av片| 亚洲理论在线观看| 久久av老司机精品网站导航| 欧美伊人久久久久久久久影院| 国产一级精品aaaaa看| 久久亚洲免费| 欧美日韩视频一区二区三区| 欧美专区日韩视频| 欧美高清视频一区二区| 久久久精品tv| 欧美午夜欧美| 欧美大色视频| 韩国av一区二区三区在线观看| 亚洲视频专区在线| 亚洲无限乱码一二三四麻| 久久亚洲一区二区| 国产一区二区三区av电影| 亚洲女人天堂av| 亚洲美女av网站| 欧美色视频在线| 亚洲香蕉伊综合在人在线视看| 99国产一区| 欧美日韩一区成人| 欧美午夜电影完整版| 日韩视频在线一区二区| 中日韩男男gay无套| 欧美视频免费在线| 一区二区久久久久| 久久欧美肥婆一二区| 欧美大秀在线观看| av成人老司机| 国产视频综合在线| 麻豆精品精品国产自在97香蕉| 亚洲电影第三页| 亚洲视频精品在线| 国内精品久久久久影院优 | 亚洲国产精品成人一区二区| 看欧美日韩国产| 中文精品视频| 亚洲国产清纯| 久久gogo国模裸体人体| 亚洲国产日韩在线一区模特| 欧美日韩视频在线一区二区观看视频 | 欧美国产精品中文字幕| 亚洲无限av看| 亚洲激情婷婷| 在线一区观看| 国内欧美视频一区二区| 欧美日韩高清不卡| 亚洲线精品一区二区三区八戒| 欧美在线精品一区| 一区二区三区四区精品| 永久免费精品影视网站| 国产精品国产馆在线真实露脸 | 夜夜嗨av一区二区三区四区| 国产精品亚洲аv天堂网| 免费久久99精品国产| 欧美一区91| 久久国产福利国产秒拍| 亚洲一区二区三区中文字幕在线 | 亚洲欧美精品在线观看| 亚洲精品一区中文| 亚洲国产天堂久久综合| 亚洲国产美女| 亚洲电影免费观看高清| 伊人激情综合| 亚洲国产99|