System.gc()

1.垃圾收集算法的核心思想

  Java語言建立了垃圾收集機制,用以跟蹤正在使用的對象和發現並回收不再使用(引用)的對象。該機制可以有效防範動態內存分配中可能發生的兩個危險:因內存垃圾過多而引發的內存耗盡,以及不恰當的內存釋放所造成的內存非法引用。

  垃圾收集算法的核心思想是:對虛擬機可用內存空間,即堆空間中的對象進行識別,如果對象正在被引用,那麼稱其爲存活對象,反之,如果對象不再被引用,則爲垃圾對象,可以回收其佔據的空間,用於再分配。垃圾收集算法的選擇和垃圾收集系統參數的合理調節直接影響着系統性能,因此需要開發人員做比較深入的瞭解。

2.觸發主GC(Garbage Collector)的條件

  JVM進行次GC的頻率很高,但因爲這種GC佔用時間極短,所以對系統產生的影響不大。更值得關注的是主GC的觸發條件,因爲它對系統影響很明顯。總的來說,有兩個條件會觸發主GC:

  ①當應用程序空閒時,即沒有應用線程在運行時,GC會被調用。因爲GC在優先級最低的線程中進行,所以當應用忙時,GC線程就不會被調用,但以下條件除外。

  ②Java堆內存不足時,GC會被調用。當應用線程在運行,並在運行過程中創建新對象,若這時內存空間不足,JVM就會強制地調用GC線程,以便回收內存用於新的分配。若GC一次之後仍不能滿足內存分配的要求,JVM會再進行兩次GC作進一步的嘗試,若仍無法滿足要求,則 JVM將報“out of memory”的錯誤,Java應用將停止。

  由於是否進行主GC由JVM根據系統環境決定,而系統環境在不斷的變化當中,所以主GC的運行具有不確定性,無法預計它何時必然出現,但可以確定的是對一個長期運行的應用來說,其主GC是反覆進行的。

3.減少GC開銷的措施

  根據上述GC的機制,程序的運行會直接影響系統環境的變化,從而影響GC的觸發。若不針對GC的特點進行設計和編碼,就會出現內存駐留等一系列負面影響。爲了避免這些影響,基本的原則就是儘可能地減少垃圾和減少GC過程中的開銷。具體措施包括以下幾個方面:

  (1)不要顯式調用System.gc()

  此函數建議JVM進行主GC,雖然只是建議而非一定,但很多情況下它會觸發主GC,從而增加主GC的頻率,也即增加了間歇性停頓的次數。

  (2)儘量減少臨時對象的使用

  臨時對象在跳出函數調用後,會成爲垃圾,少用臨時變量就相當於減少了垃圾的產生,從而延長了出現上述第二個觸發條件出現的時間,減少了主GC的機會。

  (3)對象不用時最好顯式置爲Null

  一般而言,爲Null的對象都會被作爲垃圾處理,所以將不用的對象顯式地設爲Null,有利於GC收集器判定垃圾,從而提高了GC的效率。

  (4)儘量使用StringBuffer,而不用String來累加字符串(詳見blog另一篇文章JAVA中String與StringBuffer)

  由於String是固定長的字符串對象,累加String對象時,並非在一個String對象中擴增,而是重新創建新的String對象,如 Str5=Str1+Str2+Str3+Str4,這條語句執行過程中會產生多個垃圾對象,因爲對次作“+”操作時都必須創建新的String對象,但這些過渡對象對系統來說是沒有實際意義的,只會增加更多的垃圾。避免這種情況可以改用StringBuffer來累加字符串,因StringBuffer 是可變長的,它在原有基礎上進行擴增,不會產生中間對象。

  (5)能用基本類型如Int,Long,就不用Integer,Long對象

  基本類型變量佔用的內存資源比相應對象佔用的少得多,如果沒有必要,最好使用基本變量。

  (6)儘量少用靜態對象變量

  靜態變量屬於全局變量,不會被GC回收,它們會一直佔用內存。

  (7)分散對象創建或刪除的時間

  集中在短時間內大量創建新對象,特別是大對象,會導致突然需要大量內存,JVM在面臨這種情況時,只能進行主GC,以回收內存或整合內存碎片, 從而增加主GC的頻率。集中刪除對象,道理也是一樣的。它使得突然出現了大量的垃圾對象,空閒空間必然減少,從而大大增加了下一次創建新對象時強制主GC 的機會。

        很多人把Java的“效率低下”歸咎於不能自由管理內存,但我們也知道將內存管理封裝起來的好處,這裏就不贅述。

        Java中的內存分配是隨着new一個新的對象來實現的,這個很簡單,而且也還是有一些可以“改進”內存回收的機制的,其中最顯眼的就是這個System.gc()函數。乍一看這個函數似乎是可以進行垃圾回收的,可事實並不是那麼簡單。其實這個gc()函數的作用只是提醒虛擬機:程序員希望進行一次垃圾回收。但是它不能保證垃圾回收一定會進行,而且具體什麼時候進行是取決於具體的虛擬機的,不同的虛擬機有不同的對策。那麼下一個問題就是:gc()進行回收的準則是什麼?也就是說什麼樣的對象可以被回收?簡單來說就是:沒有被任何可達變量指向的對象。這裏的可達是我發明的……意思就是能夠找到的,那什麼樣的是不可達的呢?比如說:

       a.v = b;
       b.v = c;
       a.v = d;
       看一下這段代碼:
       第一行:對象a的變量v指向了對象b
       第二行:對象b的變量v指向了對象c
       第六行:對象a的變量v指向了變量d。

       這個時候,雖然變量c指向的對象有c 以及b.v指向它,但是它們都已經不可達了,爲什麼?因爲唯一可以找到它們的是a.v,但是現在a.v指向了d,所以他們就是不可達的了。理由也很直觀:沒有任何可達變量指向你,你還有活下去的理由嗎?你就算活下去誰能找得到你呢?所以說,C++中將釋放了的指針置爲null的習慣要保留到Java中,因爲這有可能是你釋放內存的唯一途徑。

       最後的箴言:不要頻繁使用gc函數。我的建議是:保持代碼健壯(記得將不用的變量置爲null),讓虛擬機去管理內存

發佈了21 篇原創文章 · 獲贊 0 · 訪問量 3萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章