java的垃圾回收

目錄

 

什麼是Java的垃圾回收機制

垃圾回收機制的意義

內存泄漏與內存溢出

概念

內存溢出的原因

解決內存溢出的方法

垃圾回收

如何確定哪些對象需要進行回收

什麼時候進行回收操作

如何回收

標記-清除算法

標記-整理算法

複製算法

分代算法

新生代

老年代

永久代

垃圾收集器


什麼是Java的垃圾回收機制

垃圾回收機制,簡稱 GC

  • Java 語言不需要程序員直接控制內存回收,由 JVM 在後臺自動回收不再使用的內存
  • 提高編程效率
  • 保護程序的完整性
  • JVM 需要跟蹤程序中有用的對象,確定哪些是無用的,影響性能

Java垃圾回收與c語言的垃圾回收最大的區別在於:java的垃圾回收由jvm在後臺自動回收不在使用的內存,而C語言需要人爲的控制內存的回收。

垃圾回收機制的意義

內存也只是一個容器,如果說一直往內存放數據,而不進行管理與清除回收的話,總有一天,這塊區域會被完全佔滿,而且在這個過程中還可能會出現兩個問題:內存泄漏與內存溢出。

內存泄漏與內存溢出

概念

內存泄漏:應用程序不再使用對象,但是垃圾回收器無法刪除它們,因爲它們已被引用。

內存溢出:指程序申請內存時,沒有足夠的內存供申請者使用,或者說,給了你一塊存儲int類型數據的存儲空間,但是你卻存儲long類型的數據,那麼結果就是內存不夠用,此時就會報錯OOM,即所謂的內存溢出。

大量的內存泄漏的後果就是會造成內存溢出,但是內存溢出不一定是因內存泄漏造成的。

內存溢出的原因

1.內存中加載的數據量過於龐大,如一次從數據庫取出過多數據; 
2.集合類中有對對象的引用,使用完後未清空,使得JVM不能回收; 
3.代碼中存在死循環或循環產生過多重複的對象實體; 
4.使用的第三方軟件中的BUG; 
5.啓動參數內存值設定的過小

解決內存溢出的方法

  • 修改JVM啓動參數

    (-Xms:設置初始分配大小,   -Xmx:最大分配內存

  • 檢查錯誤日誌
  • 對代碼進行分析,查找可能會造成內存溢出的地方

垃圾回收

如何確定哪些對象需要進行回收

此處有兩種方法來判斷:引用計數法和可達性分析算法

引用計數發:給對象添加一個引用計數器,如果有對該對象引用時,那麼計數器加1,引用失效時,計數器減1.但是現在jvm並沒有使用這個算法,因爲不能解決對象間循環引用的問題

可達性分析算法:通過判斷對象的引用鏈是否可達來決定對象是否可以被回收。程序把所有的引用關係看成一張圖,由GC Roots的對象作爲起始點,從這些節點開始向下搜索,搜索的路徑稱爲引用鏈,當一個對象到GC Roots沒有任何一個引用鏈時,則認爲此對象不可用。

此圖中雖然Object5還保留有對6和7的引用,但是已經沒有引用鏈通向GC Roots,所以認爲該對象已經沒有引用。

在java中可以作爲GC Roots的對象:

  • 虛擬機棧(棧幀中的本地變量表)中引用的對象;
  • 方法區中類靜態屬性引用的對象;
  • 方法區中常量引用的對象;
  • 本地方法棧中 JNI(Native 方法)引用的對象。

什麼時候進行回收操作

  • CPU空閒的時候自動進行回收
  • 堆內存滿了之後
  • 主動調用System.gc()

如何回收

標記-清除算法

最基礎的一種算法,分爲兩步。第一步進行標記,第二步進行回收。經過分析後對可以回收的對象進行標記,而後回收被標記的對象。

優點:簡單易懂

缺點:回收之後會產生不連續的內存碎片,影響存儲。

執行如下:

回收之前

執行之後:

標記-整理算法

與標記-清除算法類似,,區別在於對可回收對象回收後還會對存貨對象進行整理,不會產生不連續的內存碎片。

執行如下:

回收之前:

回收之後:

複製算法

將內存分爲大小相等的兩塊,每次只使用其中的一塊,垃圾收集時,遍歷該內存空間內所有的對象,將還存活的對象複製到另一塊內存中,然後清除這一塊的所有的對象。這樣就不用考慮會產生不連續的內存碎片,只是原本的空間會被分爲兩份,導致可用空間變小,回收頻率會增加

執行如下:

回收之前:

回收之後:

分代算法

   分代算法其實並不是一個新的算法,只是不同的對象生命週期不同,故採取不同的算法回收已提高效率。

根據生命週期的不同,將內存劃分爲:年輕代,老年代,永久代。

新生代

大部分新產生的對象都是存放在新生代中,目的就是快速的回收那些生命週期較短的對象,這種回收也稱爲Minor GC,使用的算法爲複製算法。

新生代又劃分爲3部分,一個Eden區和兩個Survivor區(Survivor1和Survivor2),默認情況下內存大小比例爲8:1:1。新產生的大部分對象都是存在於Eden區,當發生回收時,會將存活對象複製到Survivor1中,然後清空Eden區。當Survivor1也存滿時,會將Survivor1與Eden區的存活對象複製到Survivor2中,清空Eden與Survivor1。

出現以下兩種情況時,則不再將存活對象複製到Survivor2而是老年代:

1)、當Survivor2內存不夠存放Eden與Survivor1的存活對象時,會直接複製到老年代

2)、在Survivor中經過多次GC後依然存活的對象,默認是15次。

由於新生代的對象生命週期較短,產生對象的速度也快,所以Minor GC頻率高,但是這種方法不會影響到老年代的GC。

老年代

前面說到大部分的新生的對象都是存入到新生代,當然還有例外,那就是較大的新生對象會直接放入老年代而不是新生代,因爲新生代與老年代在配置時,新生代的內存會遠小於老年代,如果對象較大的話可能放不下。

老年代中存儲的對象還有就是經過新生代多次GC後依舊存活的對象,所以老年代中存儲的對象生命週期都比較長,故使用的算法爲標記清除和標記整理算法,這種回收也稱爲Full GC。

Full  GC的頻率較少,但是Full GC是針對整個堆內存的回收,耗時長。

永久代

JDK8取消了永久代,誕生了元空間。元空間與永久代的本質類似,但是有個最大的區別就是:元空間使用的內存區域不在虛擬機,而是本地空間。

垃圾收集器

新生代使用的:Serial、ParNew、Parallel Scavenge

老年代使用的:Serial Old、Parallel Old、CMS

還有一個堆回收器:G1

 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章