對於記憶體洩漏問題的簡單認知

恆222發表於2019-04-16

看了很多別人對記憶體洩漏和記憶體溢位的講解,也看到很多對別人觀點的辯駁。但可能正是因為看的太多了,總感覺有點亂,所以還是自己總結下好。下面是我對於記憶體洩漏的粗淺認識。

一、記憶體洩漏簡介

首先要明確在計算機程式設計中,記憶體洩漏並非是指記憶體在物理上的消失。而是在應用程式分配了某段記憶體之後,由於設計錯誤或是某些不明確的bug,導致應用程式在未釋放該段記憶體之後就失去了對該段記憶體的控制,從而造成了記憶體的浪費。

一些黑客能通過一段攻擊程式碼來攻擊能被公眾訪問的網路伺服器或路由器等,使之產生記憶體洩漏或記憶體溢位

二、記憶體洩漏的後果

記憶體洩漏最直接的表現就是導致計算機或伺服器效能降低,但是一般不會馬上引起嚴重的狀況。

需要明確的是,記憶體消耗持續增加不一定表明是記憶體洩漏,可能是有其他原因引起的。如程式設計者在設計程式時假設儲存器總是足夠執行特定的工作,而申請不合理的大量儲存器。

這是由於大多數的現代計算機作業系統都有儲存在RAM晶片中主記憶體和儲存在次級儲存裝置如硬碟中的虛擬記憶體,記憶體分配是動態的——每個程式根據要求獲得相應的記憶體。

訪問活躍的頁面檔案被轉移到主記憶體以提高訪問速度;反之,訪問不活躍的頁面檔案被轉移到次級儲存裝置。當一個簡單的程式消耗大量的記憶體時,它通常佔用越來越多的主記憶體,使其他程式轉到次級儲存裝置,使系統的執行效率大大降低。

甚至在有記憶體洩漏的程式終止後,其他程式需要相當長的時間才能切換到主記憶體,恢復原來的執行效率。

注意:第一個遭遇記憶體問題的程式不一定是引起記憶體洩露的程式,需要經過具體排查來確定是哪個程式或程式導致的記憶體洩漏

記憶體洩漏在以下場景當中會顯得很嚴重:

  • 程式執行後就置之不理(比如伺服器的後臺任務)
  • 頻繁的分配新記憶體(比如遊戲執行)
  • 程式中止之後也不會被釋放的記憶體(例如共享記憶體)
  • 剩餘記憶體有限的時候
  • 系統關鍵驅動發生記憶體洩漏

三、容易引起記憶體洩露的操作

Java等語言中的垃圾回收機制能有效防止記憶體洩漏,像在沒有垃圾回收的程式語言,如C和C++中,儲存器洩露是很常見的錯誤(不過這兩種語言有垃圾回收的庫)

——垃圾回收
垃圾回收的設計思想,就是用不同級別的“訪問性”來標識儲存器中的物件,儲存器管理器按照釋放級別來優先釋放訪問性較低的物件。
強引用的物件或者間接和一組強引用相關的物件就表示該物件的訪問性較強

雖說Java有GC,但並不是就此安然無恙,穩如老狗了。以下這些這些場景不注意的話,還是容易造成記憶體洩漏問題的:

  • 靜態集合類的使用(例如 HashMap、Vector),這些靜態變數的生命週期和應用程式的生命週期一致。 可以使用 jdk8 中ConCurrentHashMap類(據說這個類還是有記憶體洩漏的風險,jdk9好像解決了,這個可以自行去了解)。

  • 監聽器。 釋放物件的時候記得刪除監聽器

  • 各種資源的連線沒有釋放關閉(如資料庫連線、網路連線等)。

  • 迴圈中建立大量無用物件

  • 泛用單例模式,在單例模式中持有外部物件的引用

  • 內部類和外部模組的引用

四、如何減少記憶體洩漏的發生

筆者的建議如下:
1. 平時養成良好的程式設計習慣,這是最重要的

2. 尋求測試工具的幫助

3. 注意 事件監聽、回撥函式、靜態集合物件這幾個記憶體洩漏大戶

分享一篇有趣的文章內涵系列 | 30 張令程式設計師淚流滿面的趣圖

相關文章