Java 垃圾回收機制

本文主要圍繞如下幾個問題展開:

  1. 什麼是堆內存
  2. 垃圾定義
  3. 回收的方案
  4. 分代回收垃圾機制

概述

垃圾回收機制,Java很是重要的特性之一,它讓開發者無需關注空間的建立和釋放,而是以守護進程的形式在後臺自動回收垃圾。算法

堆內存

堆,是在JVM啓動時建立的,屬於JVM運行時數據區的重要組成部分,主要用來維護運行時數據,如運行過程當中建立的實例對象和數據。若是動態建立的對象沒有獲得及時回收,持續堆積,最後會致使堆空間被佔滿,形成內存溢出。設計

Java提供的垃圾回收機制,在後臺建立一個守護進程。在內存緊張時自動執行垃圾回收,從而保證程序的正常運行。對象

垃圾定義

所謂「垃圾」,指全部再也不存活的對象進程

垃圾判斷

常見判斷對象是否存活的兩種方法:圖片

  • 引用計數法 爲每個對象建立一個引用計數器,用來存儲該對象被引用的個數。當計數爲0時,意味着該對象再也不被使用,能夠認爲**「對象死亡」**。可是該方案存在嚴重的問題:沒法檢測「循環引用」,當兩個對象相互引用時,即便他倆不被其餘對象引用時,他倆的計數都不會爲0,所以永遠不會被回收。

實際上,Java裏沒有采用這樣的方案來斷定對象的「存活性」。內存

  • 可達性分析 目前主流的開發語言都採用對象存活性判斷方案,基本思路是:把全部引用的對象想象成一顆樹,從樹的根節點(GC Roots)出發,持續遍歷找出全部鏈接的樹枝對象(包含葉子節點),這些對象成爲「存活對象」或「可達對象」。其他的對象被成爲「死亡對象」或「不可達對象」,亦或者「垃圾」。

參考下圖,object五、object六、object7爲不可達對象,視爲「垃圾」,會被垃圾回收器回收 輸入圖片說明開發

GC Roots

GC Roots自己必定是可達的,從他們出發遍歷到的對象才能保證必定可達。 Java裏存在如下四種必定可達對象:虛擬機

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

垃圾回收方案

備註:下述圖中,黑色表明垃圾對象灰色表明存活對象綠色表明空白空間it

標記-清理

第一步,標記,利用可達性遍歷堆內存時,把存活對象和垃圾對象進行標記(以下圖所示) 輸入圖片說明io

第二步,清理,把標記爲垃圾對象進行清空,釋放所佔空間 輸入圖片說明

總結,該方案簡單方便,但容易產生內存碎片。

標記-整理

基於標記-清理方案,在清理時將全部存活的對象集中到一塊兒,造成一個連續使用的內存空間,以下圖所示: 輸入圖片說明

總結,標記-清理、標記-整理兩種方案,適合存活對象多、垃圾少的狀況,只須要清理較少的垃圾,挪動一下存活對象就能夠了。

複製

這種比較粗暴,將堆內存一分爲二成兩部分,一段時間內只容許在其中一塊內存上進行分配,當該內存塊被分配完後,則執行垃圾回收:把全部存活對象複製到另外一塊內存裏,而後直接清空當前內存。

輸入圖片說明

輸入圖片說明

總結,這種作法不易產生碎片,簡單粗暴;可是,它只容許一段時間內只能使用一部份內存,超過這部份內存的話就有頻繁的複製清空。這種方案適合存活對象少、垃圾多的狀況,在複製只需移動少許的存活對象。

分代回收機制

上述講到三種內存回收方案,那Java中是如何選擇利用這三種回收算法呢?

堆結構

Java 堆空間(Heap)分紅三部分,這三部分存儲三類數據:

  • 新建立的對象,新生代區域,特色:存活對象少、垃圾多(複製)
  • 存活了一段時間的對象,老年代區域,特色:存活對象多、垃圾少(標記-整理)
  • 永久存在的對象,永久代區域,特色:永遠存活,不須要垃圾回收(Java8中已經刪除了永久代,使用了元空間Meta Space)

總結,常規的Java堆至少包括了新生代和老年代兩塊內存區域:

  • 新生代:存活對象少、垃圾多
  • 老年代:存活對象多、垃圾少

新生代:複製回收機制

對於新生代區域,因爲每次GC都會有大量新對象死去,存活的較少。所以採用複製回收機制,GC時只把少數存活的對象複製過去便可。

複製算法設計:

  1. 內存1:1 每次只使用一半的內存,當這一半滿了後,就進行垃圾回收,把存活的對象直接複製到另外一半內存,並清空當前這一半的內存。

缺陷:至關於只有一半的內存可用,對於新生代而言,新對象會頻繁地進行建立,若是隻有一半的可用內存,會持續 不斷地進行垃圾回收工做,影響了程序的正常運行。

  1. 內存9:1

最開始使用9的內存,當9快滿時執行復制回收機制,把9中存活的對象複製到1區,並清空9區。

缺陷:因爲內存空間比例相差較大,當把9區存活的對象複製到1區時,頗有可能1區放不下,此時不得不把對象移到老年區。這就意味着,可能會有一部分並不老的9區對象因爲1區放不下而被放到老年區,破壞了老年區的規則。

  1. 內存8:1:1(Eden:SurviorA:SurvivorB)

工做原理:

  • Eden區最大,對外提供堆內存。當Eden區快滿時,進行Minor GC,把存活對象放入Survivor A區,清空Eden區;
  • Eden區被清空後,繼續對外提供堆內存
  • 當Eden區被填滿時,對Eden和Survivor A同時進行Minor GC,把存活的對象複製到Survivor B,同時清空Eden、Survivor A
  • Eden繼續對外提供堆內存,並重覆上一步工做
  • 當某個Survivor被填滿,且仍有對象未被複制完畢時,或者某些對象在反覆Survive 15次左右時,則把這部分剩餘對象放到Old區(老年代)
  • 當Old區也被填滿時,進行Major GC,對Old區進行垃圾回收

老年代:標記整理回收機制

老年代通常存放存活時間較久的對象,因此每次GC時,存活對象多,每次只要少數對象被回收。所以,根據不一樣回收機制的特色,這裏選擇標記整理回收機制,僅僅經過少許地移動對象就能清理垃圾,並且不存在內存碎片。

相關文章
相關標籤/搜索