1. 程式人生 > >解釋:記憶體溢位、記憶體洩露、記憶體越界、緩衝區溢位、棧溢位

解釋:記憶體溢位、記憶體洩露、記憶體越界、緩衝區溢位、棧溢位

記憶體溢位就是你要求分配的記憶體超出了系統能給你的,系統不能滿足

需求,於是產生溢位。

================================================================

記憶體洩漏是指你向系統申請分配記憶體進行使用(new),可是使用
完了以後卻不歸還(delete),結果你申請到的那塊記憶體你自己也不能
再訪問(也許你把它的地址給弄丟了),而系統也不能再次將它分配
給需要的程式。一個盤子用盡各種方法只能裝4 個果子,你裝了5
個,結果掉倒地上不能吃了。這就是溢位!比方說棧,棧滿時再做進
棧必定產生空間溢位,叫上溢,棧空時再做退棧也產生空間溢位,稱
為下溢。就是分配的記憶體不足以放下資料項序列,稱為記憶體溢位.

以發生的方式來分類,記憶體洩漏可以分為4 類:
1. 常發性記憶體洩漏。發生記憶體洩漏的程式碼會被多次執行到,每次被
執行的時候都會導致一塊記憶體洩漏。
2. 偶發性記憶體洩漏。發生記憶體洩漏的程式碼只有在某些特定環境或操
作過程下才會發生。常發性和偶發性是相對的。對於特定的環境,偶
發性的也許就變成了常發性的。所以測試環境和測試方法對檢測記憶體
洩漏至關重要。
3. 一次性記憶體洩漏。發生記憶體洩漏的程式碼只會被執行一次,或者由
於演算法上的缺陷,導致總會有一塊僅且一塊記憶體發生洩漏。比如,在
類的建構函式中分配記憶體,在解構函式中卻沒有釋放該記憶體,所以內
存洩漏只會發生一次。
4. 隱式記憶體洩漏。程式在執行過程中不停的分配記憶體,但是直到結

束的時候才釋放記憶體。嚴格的說這裡並沒有發生記憶體洩漏,因為最終
程式釋放了所有申請的記憶體。但是對於一個伺服器程式,需要執行幾
天,幾周甚至幾個月,不及時釋放記憶體也可能導致最終耗盡系統的所
有記憶體。所以,我們稱這類記憶體洩漏為隱式記憶體洩漏。
從使用者使用程式的角度來看,記憶體洩漏本身不會產生什麼危害,作為
一般的使用者,根本感覺不到記憶體洩漏的存在。真正有危害的是記憶體洩
漏的堆積,這會最終消耗盡系統所有的記憶體。從這個角度來說,一次
性記憶體洩漏並沒有什麼危害,因為它不會堆積,而隱式記憶體洩漏危害

性則非常大,因為較之於常發性和偶發性記憶體,洩漏它更難被檢測到

=================================================================

記憶體越界:

何謂記憶體訪問越界,簡單的說,你向系統申請了一塊記憶體,在使用這塊記憶體的時候,超出了你申請的範圍。

記憶體越界使用,這樣的錯誤引起的問題存在極大的不確定性,有時大,有時小,有時可能不會對程式的執行產生影響,正是這種不易重現的錯誤,才是最致命的,一旦出錯破壞性極大。


什麼原因會造成記憶體越界使用呢?有以下幾種情況,可供參考:
例1:
        char buf[32] = {0};
        for(int i=0; i<n; i++)// n < 32 or n > 32
        {
            buf[i] = 'x';
        }
        ....        
例2:
        char buf[32] = {0};
        string str = "this is a test sting !!!!";
        sprintf(buf, "this is a test buf!string:%s", str.c_str()); //out of buffer space
        ....    
例3:
        string str = "this is a test string!!!!";
        char buf[16] = {0};
        strcpy(buf, str.c_str()); //out of buffer space
        
類似的還存在隱患的函式還有:strcat,vsprintf等
同樣,memcpy, memset, memmove等一些記憶體操作函式在使用時也一定要注意。
        
當這樣的程式碼一旦執行,錯誤就在所難免,會帶來的後果也是不確定的,通常可能會造成如下後果:

1.破壞了堆中的記憶體分配資訊資料,特別是動態分配的記憶體塊的記憶體資訊資料,因為作業系統在分配和釋放記憶體塊時需要訪問該資料,一旦該資料被破壞,以下的幾種情況都可能會出現。 
        *** glibc detected *** free(): invalid pointer:
        *** glibc detected *** malloc(): memory corruption:
        *** glibc detected *** double free or corruption (out): 0x00000000005c18a0 ***
        *** glibc detected *** corrupted double-linked list: 0x00000000005ab150 ***        

2.破壞了程式自己的其他物件的記憶體空間,這種破壞會影響程式執行的不正確性,當然也會誘發coredump,如破壞了指標資料。

3.破壞了空閒記憶體塊,很幸運,這樣不會產生什麼問題,但誰知道什麼時候不幸會降臨呢?

通常,程式碼錯誤被激發也是偶然的,也就是說之前你的程式一直正常,可能由於你為類增加了兩個成員變數,或者改變了某一部分程式碼,coredump就頻繁發生,而你增加的程式碼絕不會有任何問題,這時你就應該考慮是否是某些記憶體被破壞了。

排查的原則,首先是保證能重現錯誤,根據錯誤估計可能的環節,逐步裁減程式碼,縮小排查空間。
檢查所有的記憶體操作函式,檢查記憶體越界的可能。常用的記憶體操作函式:
sprintf snprintf 
vsprintf vsnprintf
strcpy strncpy strcat 
memcpy memmove memset bcopy

如果有用到自己編寫的動態庫的情況,要確保動態庫的編譯與程式編譯的環境一致。

=================================================================

緩衝區溢位:

緩衝區溢位是指當計算機向緩衝區內填充資料位數時超過了緩衝區本身的容量溢位的資料覆蓋在合法資料上,理想的情況是程式檢查資料長度並不允許輸入超過緩衝區長度的字元,但是絕大多數程式都會假設資料長度總是與所分配的儲存空間相匹配,這就為緩衝區溢位埋下隱患.作業系統所使用的緩衝區 又被稱為"堆疊". 在各個操作程序之間,指令會被臨時儲存在"堆疊"當中,"堆疊"也會出現緩衝區溢位。

棧溢位:

 棧溢位就是緩衝區溢位的一種。 由於緩衝區溢位而使得有用的儲存單元被改寫,往往會引發不可預料的後果。程式在執行過程中,為了臨時存取資料的需要,一般都要分配一些記憶體空間,通常稱這些空間為緩衝區。如果向緩衝區中寫入超過其本身長度的資料,以致於緩衝區無法容納,就會造成緩衝區以外的儲存單元被改寫,這種現象就稱為緩衝區溢位。

  棧溢位就是緩衝區溢位的一種。