首頁>Club>
7
回覆列表
  • 1 # 豐偉609

    記憶體問題一直以來都是C/C++開發中比較麻煩的問題,總的來講,可以分成記憶體碎片、記憶體洩漏和記憶體越界這幾類其中,以記憶體越界最為複雜,而且難以定位。下面我們將透過技術手段逐一分析並解決這三個問題。

    一、作業系統的記憶體管理

    程序的虛擬記憶體空間是地址是連續的,整個記憶體空間以頁為單位進行劃分,並不是每個頁當前都映射了物理地址(commited),應用程式透過作業系統提供的API對記憶體進行管理,以頁為單位,將虛擬記憶體地址對映到物理地址或者解除虛擬地址和物理地址的對映,某些情況下作業系統也會自動進行這個過程:例如當物理記憶體不足時,會根據一定的策略將部分虛擬空間的資料儲存到硬碟上,同時解除虛擬空間和物理地址的對映,把空出來的物理地址對映到當前cpu需要訪問的虛擬空間(拆東牆補西牆)。

    一、記憶體碎片

    當應用程式透過作業系統提供的API動態的申請記憶體和歸還記憶體,那麼難以避免的就會造成當前已分配的記憶體在程序中佔用著不連續的虛擬空間,反過來,不被應用程式佔用的空閒記憶體空間也是不連續的,那麼即便這些空閒記憶體的總量大於下次應用程式申請的空間大小,因為不連續,所以也無法成功申請到。空閒記憶體不連續的問題越嚴重(不連續的記憶體快越多,每塊記憶體越小),分配不成功的機率就越大。

    我們透過以下幾個技術手段解決這個問題:

    1、 建立小記憶體池機制,相同大小或者大小接近的記憶體都放到同一個記憶體池,這樣從記憶體池的每個記憶體塊之間浪費的記憶體會非常小或者沒有浪費。

    2、記憶體池向系統申請記憶體時都以頁為單位進行申請大塊記憶體(Block),記憶體池對外分配記憶體也以優先使用使用率最高的Block,這樣會有更高的機率將整個Block歸還系統。

    二、記憶體洩漏

    記憶體洩漏產生的原因比較簡單明確,就是申請的記憶體已經不需要再次被使用了,但是並沒有歸還給記憶體分配器(管理器)或者系統。正確的使用智慧指標可以有效的避免記憶體洩漏的發生,這是推薦的方式。我們下面介紹另外一種在X86下解決記憶體洩漏的方式。

    我們在每次進行記憶體分配的時候將呼叫的堆疊記錄下來,並且和分配的記憶體塊進行關聯,這樣,分配的每一塊記憶體都有一個明確的來源,這樣我們可以隨時統計哪些地方分配了多少記憶體(記憶體快照)。我們可以在需要時生成一個記憶體快照,透過對比記憶體快照可以確定這段時間內哪些地方分配或者釋放了多少記憶體。

    三、記憶體越界

    記憶體越界是比較難以定位,並且也是非常危險的一個問題。記憶體越界通常發生的場景有(不限於以下):

    1、讀寫超出記憶體的有效範圍,例如陣列下標越界,指標加減運算錯誤

    2、已釋放的記憶體,其指標仍然被某些地方非法持有(野指標),並且進行了讀或者寫

    記憶體越界讀產生的危險總體而言要小於越界寫。因為資料讀錯了,很大機率會立刻暴露問題,比較容易定位,但是越界寫的話,很有可能會在後續某個完全不相干的位置或者時間裡出現問題,這時已經很難再找回之前寫壞資料的現場了。下面我們討論一種定位記憶體越界寫的方案。

    記憶體越界寫主要的問題是往往寫的時候沒有引發異常,如果我們能在越界寫的時候讓程式丟擲異常,那麼這個問題基本就定位了。為此,我們可以令記憶體分配器每次分配都返回一個永遠不重複的地址,這個地址指向的記憶體前面和後面的一頁或者幾頁的地址空間都是沒有對映到物理記憶體的,那麼當對這塊記憶體的讀寫超出邊界的時候就會觸發暴力讀寫的異常。同樣,當這塊記憶體被釋放以後,我們將這塊記憶體所指向的虛擬地址和物理地址的對映解除,那麼後續嘗試對這個地址進行讀寫的時候也會觸發暴力讀寫的異常,這樣我們就可以快速定位問題所在。

  • 2 # 揮舞春天

    1>少用動態記憶體分配的函式(儘量使用棧空間)

       2>分配記憶體和釋放的記憶體儘量在同一個函式中(避免記憶體洩漏)

       3>儘量一次性申請較大的記憶體2的指數次冪大小的記憶體空間,而不要反覆申請小記憶體(少進行記憶體的分割)

       4>使用記憶體池來減少使用堆記憶體引起的記憶體碎片

       5>儘可能少地申請空間

       6>儘量少使用堆上的記憶體空間~

       7>做記憶體池,也就是自己一次申請一塊足夠大的空間,然後自己來管理,用於大量頻繁地new/delete操作。

  • 中秋節和大豐收的關聯?
  • 怎麼幫貓咪辦理火車託運?