今天在使用OSS軟體【對象存儲(Object Storage Service,簡稱OSS),是阿裡雲對外提供的海量、安全和高可靠的雲存儲服務。RESTful API的平台無關性,容量和處理能力的彈性擴充,按實際容量付費真正使您專注于核心業務。】上傳官網所需的用戶端資源時,發現上傳檔案過多時,會出現速度慢,類似卡死、閃退這種現象。經過檢視程序管理器發現它占用的記憶體太多,經驗豐富的主程說這是記憶體洩漏造成的。我這才認識到,原來寫代碼時檢查的記憶體洩漏,是為了防止占用空間大啊!于是,傳完資源後,趕緊百度了一下 。沒想到啊,一個經常做的操作,讓我對記憶體洩漏有了一個更深的了解。
記憶體洩漏也稱作“存儲滲漏”,用動态存儲配置設定函數動态開辟的空間,在使用完畢後未釋放,結果導緻一直占據該記憶體單元。直到程式結束。(其實說白了就是該記憶體空間使用完畢之後未回收)即所謂記憶體洩漏。
記憶體洩漏形象的比喻是“作業系統可提供給所有程序的存儲空間正在被某個程序榨幹”,最終結果是程式運作時間越長,占用存儲空間越來越多,最終用盡全部存儲空間,整個系統崩潰。是以“記憶體洩漏”是從作業系統的角度來看的。這裡的存儲空間并不是指實體記憶體,而是指虛拟記憶體大小,這個虛拟記憶體大小取決于磁盤交換區設定的大小。由程式申請的一塊記憶體,如果沒有任何一個指針指向它,那麼這塊記憶體就洩漏了。
記憶體洩漏分類:
常發性:發生記憶體洩漏的代碼會被多次執行到,每次被執行的時候都會導緻一塊記憶體洩漏。
偶發性:發生記憶體洩漏的代碼隻有在某些特定環境或操作過程下才會發生。常發性和偶發性是相對的。對于特定的環境,偶發性的也許就變成了常發性的。是以測試環境和測試方法對檢測記憶體洩漏至關重要。
一次性:發生記憶體洩漏的代碼隻會被執行一次,或者由于算法上的缺陷,導緻總會有一塊且僅一塊記憶體發生洩漏。比如,在類的構造函數中配置設定記憶體,在析構函數中卻沒有釋放該記憶體,是以記憶體洩漏隻會發生一次。
隐式:程式在運作過程中不停的配置設定記憶體,但是直到結束的時候才釋放記憶體。嚴格的說這裡并沒有發生記憶體洩漏,因為最終程式釋放了所有申請的記憶體。但是對于一個伺服器程式,需要運作幾天、幾周甚至幾個月,不及時釋放記憶體也可能導緻最終耗盡系統的所有記憶體。是以,我們稱這類記憶體洩漏為隐式記憶體洩漏。
記憶體洩漏危害:從使用者使用程式的角度來看,記憶體洩漏本身不會産生什麼危害,作為一般的使用者,根本感覺不到記憶體洩漏的存在。真正有危害的是記憶體洩漏的堆積,這會最終消耗盡系統所有的記憶體。從這個角度來說,一次性記憶體洩漏并沒有什麼危害,因為它不會堆積,而隐式記憶體洩漏危害性則非常大,因為較之于常發性和偶發性記憶體洩漏它更難被檢測到。
記憶體洩漏表現:
cpu資源耗盡:估計是機器沒有反應了,鍵盤,滑鼠,以及網絡等等。在中了計算機病毒的裝置上非常常見。
程序id耗盡:沒法建立新的程序了,序列槽或者telnet都沒法建立了。
硬碟耗盡: 機器要死了,交換記憶體沒法用,日志也沒法用了,死是很正常的。
記憶體洩漏或者記憶體耗盡:新的連接配接無法建立,free的記憶體比較少。發生記憶體洩漏的程式很多,但是要想産生一定的後果,就需要這個程序是無限循環的,是個服務程序。當然,核心也是無限循環的,是以,如果核心發生了記憶體洩漏,情況就更加不妙。記憶體洩漏是一種很難定位和跟蹤的錯誤,目前還沒看到有什麼好用的工具(當然,使用者空間有一些工具,有靜态分析的,也會動态分析的,但是找核心的記憶體洩漏,沒有好的開源工具)。
記憶體洩漏和對象的引用計數有很大的關系,再加上c/c++都沒有自動的垃圾回收機制,如果沒有手動釋放記憶體,問題就會出現。如果要避免這個問題,還是要從代碼上入手,良好的編碼習慣和規範,是避免錯誤的不二法門。
一般我們常說的記憶體洩漏是指堆記憶體的洩漏。
堆記憶體是指程式從堆中配置設定的,大小任意的(記憶體塊的大小可以在程式運作期決定),使用完後必須顯式釋放的記憶體。
應用程式一般使用malloc,realloc,new等函數從堆中配置設定到一塊記憶體,使用完後,程式必須負責相應的調用free或delete釋放該記憶體塊,否則,這塊記憶體就不能被再次使用,我們就說這塊記憶體洩漏了。