1. 程式人生 > 實用技巧 >12. 垃圾回收概述

12. 垃圾回收概述

1. 垃圾回收概述

背景

Java 和 C++語言的區別,就在於垃圾收集技術和記憶體動態分配上,C語言沒有垃圾收集技術,需要我們手動的收集。

垃圾收集,不是Java語言的伴生產物。早在1960年,第一門開始使用記憶體動態分配和垃圾收集技術的Lisp語言誕生。

關於垃圾收集有三個經典問題:

  • 哪些記憶體需要回收?

  • 什麼時候回收?

  • 如何回收?

垃圾收集機制是Java的招牌能力,極大地提高了開發效率。如今,垃圾收集幾乎成為現代語言的標配,即使經過如此長時間的發展,Java的垃圾收集機制仍然在不斷的演進中,不同大小的裝置、不同特徵的應用場景,對垃圾收集提出了新的挑戰,這當然也是面試的熱點。

什麼是垃圾?

垃圾是指在執行程式中沒有任何指標指向的物件,這個物件就是需要被回收的垃圾。

如果不及時對記憶體中的垃圾進行清理,那麼,這些垃圾物件所佔的記憶體空間會一直保留到應用程式結束,被保留的空間無法被其他物件使用。甚至可能導致記憶體溢位。

為什麼需要垃圾回收

  1. 對於高階語言來說,一個基本認知是如果不進行垃圾回收,記憶體遲早都會被消耗完,因為不斷地分配記憶體空間而不進行回收,就好像不停地生產生活垃圾而從來不打掃一樣。
  2. 除了釋放沒用的物件,垃圾回收也可以清除記憶體裡的記錄碎片。碎片整理將所佔用的堆記憶體移到堆的一端,以便JVM將整理出的記憶體分配給新的物件。
  3. 隨著應用程式所應付的業務越來越龐大、複雜,使用者越來越多,沒有GC就不能保證應用程式的正常進行。而經常造成STW的GC又跟不上實際的需求,所以才會不斷地嘗試對GC進行優化。

2. 早期的垃圾回收

在早期,開發人員需要手動回收記憶體

在早期的C/C++時代,垃圾回收基本上是手工進行的。開發人員可以使用new關鍵字進行記憶體申請,並使用delete關鍵字進行記憶體釋放。比如以下程式碼:

MibBridge *pBridge= new cmBaseGroupBridge();
//如果註冊失敗,使用Delete釋放該物件所佔記憶體區域
if(pBridge->Register(kDestroy)!=NO ERROR)
	delete pBridge;

這種方式可以靈活控制記憶體釋放的時間,但是會給開發人員帶來頻繁申請和釋放記憶體的管理負擔。倘若有一處記憶體區間由於程式設計師編碼的問題忘記被回收,那麼就會產生記憶體洩漏,垃圾物件永遠無法被清除,隨著系統執行時間的不斷增長,垃圾物件所耗記憶體可能持續上升,直到出現記憶體溢位並造成應用程式崩潰。

有了垃圾回收機制後,上述程式碼極有可能變成這樣

MibBridge *pBridge=new cmBaseGroupBridge(); 
pBridge->Register(kDestroy);

現在,除了Java以外,C#、Python、Ruby等語言都使用了自動垃圾回收的思想,也是未來發展趨勢,可以說這種自動化的記憶體分配和垃圾回收方式已經成為了現代開發語言必備的標準。

3. 自動記憶體管理

自動記憶體管理的優點

  1. 自動記憶體管理,無需開發人員手動參與記憶體的分配與回收,這樣降低記憶體洩漏和記憶體溢位的風險
  2. 沒有垃圾回收器,java也會和cpp一樣,各種懸垂指標,野指標,洩露問題讓你頭疼不已。
  3. 自動記憶體管理機制,將程式設計師從繁重的記憶體管理中釋放出來,可以更專心地專注於業務開發

缺點:

對於Java開發人員而言,自動記憶體管理就像是一個黑匣子,如果過度依賴於“自動”,那麼這將會是一場災難,最嚴重的就會弱化Java開發人員在程式出現記憶體溢位時定位問題和解決問題的能力。

如何避免:

  • 此時,瞭解JVM的自動記憶體分配和記憶體回收原理就顯得非常重要,只有在真正瞭解JVM是如何管理記憶體後,我們才能夠在遇見OutofMemoryError時,快速地根據錯誤異常日誌定位問題和解決問題。
  • 當需要排查各種記憶體溢位、記憶體洩漏問題時,當垃圾收整合為系統達到更高併發量的瓶頸時,我們就必須對這些“自動化”的技術實施必要的監控和調節。

GC 的作用區域範圍及頻率

垃圾收集器可以對年輕代回收,也可以對老年代回收,甚至是全棧和方法區的回收,其中,Java堆是垃圾收集器的工作重點

從次數上講:

  1. 頻繁收集Young區
  2. 較少收集Old區
  3. 基本不收集Perm區(元空間)

總結: GC主要關注於方法區和堆中的垃圾收集