1. 程式人生 > >JVM垃圾回收機制總結

JVM垃圾回收機制總結

JVM分為三個主要的子系統

(1)類載入器子系統(2)執行時資料區(3)執行引擎

1.類載入器子系統

Java的動態類載入功能是由類載入器子系統處理。當它在執行時(不是編譯時)首次引用一個類時,它載入、連結並初始化該類檔案。

1.1載入

類由此元件載入,遵循委託層次演算法載入類檔案。

  • 啟動類載入器:負責從啟動類路徑中載入類,rt.jar,優先順序比較高
  • 擴充套件類載入器:負責載入ext目錄(jre/lib)內的類
  • 應用程式類載入器:負責載入應用程式級別類路徑,涉及到路徑的環境變數等

1.2連結

  • 校驗 – 位元組碼校驗器會校驗生成的位元組碼是否正確,如果校驗失敗,我們會得到校驗錯誤
  • 準備 – 分配記憶體並初始化預設值給所有的靜態變數。
  • 解析 – 所有符號記憶體引用方法區(Method Area)原始引用所替代。

1.3初始化

這是類載入的最後階段,是所有的靜態變數被賦初始值,並且靜態塊將被執行

1.4什麼是類載入機制

Class檔案描述的各種資訊,都需要載入到虛擬機器後才能執行。虛擬機器把描述類的資料從Class檔案載入到記憶體,並對資料進行校驗、轉換解析和初始化,最終形成可以被虛擬機器直接使用的Java型別,這就是虛擬機器的類載入機制。

1.5雙親委派模型

雙親委派模型(Parents Delegation Model)要求除了頂層的啟動類載入器外,其餘載入器都應當有自己的父類載入器。類載入器之間的父子關係,通過組合關係複用。 
工作過程:如果一個類載入器收到了類載入的請求,它首先不會自己去嘗試載入這個類

,而是把這個請求委派給父類載入器完成。每個層次的類載入器都是如此,因此所有的載入請求最終都應該傳送到頂層的啟動類載入器中,只有到父載入器反饋自己無法完成這個載入請求(它的搜尋範圍沒有找到所需的類)時,子載入器才會嘗試自己去載入。

2.執行時的資料區(Runtime Data Area)

執行緒共享的資料區是堆和方法區,其他的都是執行緒私有的資料區

2.1方法區(Method Area)

  • 所有類級別的資料將被儲存在這裡,在class被載入後的一些資訊 如常量,靜態常量這些被放在這裡,在Hotspot裡面我們將它稱之為永生代。
  • 每個JVM只有一個方法區,它是一個共享的資源

2.2 堆區(Heap Area)

  • 他是最大的一塊區域,用於存放物件例項和陣列,是全域性共享的,每個JVM只有一個堆區,由於其記憶體由多個執行緒共享,所以不是執行緒安全的

2.3 棧區(Stack Area)

對每個執行緒會單獨建立一個執行時棧。不是共享資源,執行緒安全。對每個函式呼叫會在棧記憶體生成一個棧幀(Stack Frame)

  • 區域性變數陣列 – 包含多少個與方法相關的區域性變數並且相應的值將被儲存在這裡。

  • 運算元棧 – 如果需要執行任何中間操作,運算元棧作為執行時工作區去執行指令。

  • 幀資料 – 方法的所有符號都儲存在這裡。在任意異常的情況下,catch塊的資訊將會被儲存在幀資料裡面。

2.4 PC暫存器

每個執行緒都有一個單獨的PC暫存器來儲存當前執行指令的地址,一旦該指令被執行,pc暫存器會被更新至下條指令的地址。

2.5 本地方法棧

本地方法棧儲存本地方法資訊。對每一個執行緒,將建立一個單獨的本地方法棧。

3.垃圾回收機制

3.1什麼是垃圾?

java中的垃圾是指已經分配記憶體但不再有任何引用的物件。

3.2兩種垃圾判斷演算法

3.2.1引用計數法

a.演算法分析

  • 當一個物件被建立時,就將該物件例項分配給一個變數,該變數計數設定為1。
  • 當任何其它變數被賦值為這個物件的引用時,計數加1(a = b,則b引用的物件例項的計數器+1)
  • 當一個物件例項的某個引用超過了生命週期或者被設定為一個新值時,物件例項的引用計數器減1。
  • 任何引用計數器為0的物件例項可以被當作垃圾收集。

b.優缺點:

  • 優點:引用計數器可以很快的執行,對程式需要不被長時間打斷的實時環境比較有利;判斷效率高
  • 缺點:對迴圈引用的物件無法進行回收

3.2.2可達性分析法

a.演算法分析

可達性分析演算法是從離散數學中的圖論引入的,程式把所有的引用關係看作一張圖,從一個節點GC ROOT開始,尋找對應的引用節點,找到這個節點以後,繼續尋找這個節點的引用節點,當所有的引用節點尋找完畢之後,剩餘的節點則被認為是沒有被引用到的節點,即無用的節點,無用的節點將會被判定為是可回收的物件。

è¿éåå¾çæè¿°

b.可作為GC ROOTS的物件:

  1. 虛擬機器棧中引用的物件
  2. 方法區中靜態屬性引用的物件
  3. 方法區中常量引用的物件
  4. 本地方法中JNI引用的物件

3.3四種垃圾回收演算法

3.3.1 標記-清除演算法

a.演算法分析:

  •  標記:採用跟集合(GC Roots)進行掃描,對存活的物件進行標記
  •  清除:掃描整個空間中未被標記的物件,進行回收

b.缺點:由於直接回收不存活的物件,會造成記憶體碎片;會停止整個程式執行,遞迴效率效能低

3.3.2複製演算法

a.演算法分析:

  •  把記憶體分為兩塊區域:空閒區域和活動區域,第一還是標記(標記可達物件)
  • 標記之後將可達物件複製到空閒區域,將空閒區變成活動區,同時把以前的活動區物件清除掉,程式設計空閒區

b.缺點:速度快但是耗費空間,若活動區全部是活動物件,這個時候進行交換的話多佔用一倍空間也沒有什麼用處

3.3.3標記-整理演算法

a.演算法分析:

  • 標記操作和“標記-清除”演算法一致,後續操作不只是直接清理物件,而是在清理無用物件完成後讓所有存活的物件都向一端移動,並更新引用其物件的指標。

b.缺點:在標記-清除的基礎上還需進行物件的移動,成本相對較高,好處則是不會產生記憶體碎片。

3.3.4分代收集演算法

分代收集演算法是大部分JVM的垃圾收集器採用的演算法。

根據物件存活的生命週期將記憶體劃分為若干個不同的區域。一般情況下將堆區劃分為老年代(Tenured Generation)和新生代(Young Generation),在堆區之外(方法區)還有一個代就是永久代(Permanet Generation)。

  • 新生代的特點是每次垃圾收集時只有少量物件需要被回收,採用複製演算法
  • 老年代的特點是每次垃圾回收時都有大量的物件需要被回收,採用標記-整理演算法或者標記清理

3.4七個垃圾收集器

3.4.1垃圾收集器

  • Serial收集器(複製演算法):新生代單執行緒收集器,標記和清理都是單執行緒,優點是簡單高效。是client級別預設的GC方式,可以通過-XX:+UseSerialGC來強制指定。
  • Serial Old收集器(標記-整理演算法):老年代單執行緒收集器,Serial收集器的老年代版本。
  • ParNew收集器(複製演算法):是Serial收集器的多執行緒版本。
  • Parallel Scavenge收集器(複製演算法):新生代收集器,並行收集器,追求高吞吐量,高效利用CPU。吞吐量一般為99%, 吞吐量= 使用者執行緒時間/(使用者執行緒時間+GC執行緒時間)。適合後臺應用等對互動相應要求不高的場景。是server級別預設採用的GC方式,可用-XX:+UseParallelGC來強制指定,用-XX:ParallelGCThreads=4來指定執行緒數。
  • Parallel Old收集器(標記-整理演算法):是Parallel Scavenge收集器的老年代版本
  • CMS(Concurrent Mark Sweep)收集器(標記-清理演算法)高併發、低停頓,追求最短GC回收停頓時間,cpu佔用比較高,響應時間快,停頓時間短,多核cpu 追求高響應時間的選擇。
  • G1收集器:(Garbage-First)是一款面向伺服器的垃圾收集器,主要針對配備多顆處理器及大容量記憶體的機器. 以極高概率滿足GC停頓時間要求的同時,還具備高吞吐量效能特徵。

3.4.2G1

優點:

  • 整體是基於標記-整理,區域性使用複製演算法,不會產生碎片空間
  • 可以獨立管理整個堆(使用分代演算法)
  • 利用多CPU來縮短STW的時間
  • 可以預測停頓:G1把整個堆分成多個Region,然後計算每個Region裡面的垃圾大小(根據回收所獲得的空間大小和回收所需要的時間的經驗值),在後臺維護一個優先列表,每次根據允許的收集時間,優先回收價值最大的Region。

與其他比較:

  • G1的設計原則是”首先收集儘可能多的垃圾(Garbage First)“,G1並不會等記憶體耗盡(序列、並行)或者快耗盡(CMS)的時候開始垃圾收集,而是在內部採用了啟發式演算法,在老年代找出具有高收集收益的分割槽進行收集。
  • G1採用記憶體分割槽(Region)的思路,將記憶體劃分為一個個相等大小的記憶體分割槽,回收時則以分割槽為單位進行回收,存活的物件複製到另一個空閒分割槽中。
  • G1雖然也是分代收集器,但整個記憶體分割槽不存在物理上的年輕代與老年代的區別,邏輯上的
  • G1的收集都是STW停頓的(STW,Stop-The-Word)的,但年輕代和老年代的收集界限比較模糊,採用了混合(mixed)收集的方式。

3.5.記憶體分配策略

  • 物件優先分配到Eden區(新生代)
  • 大物件直接進入老年代
  • 年齡大的物件進入老年代(經過15次Minor GC依然存活)
  • 動態物件年齡判定,年齡大於等於Survivor空間相同年齡的一半,就進入老年代
  • 空間分配擔保(Minor GC之前,虛擬機器先檢查老年代最大可用的連續空間是否大於新生代所有物件總空間,如果是,GC是確保安全的。)

4.finalize()方法

  • 用途:java垃圾回收只是釋放那些經過new分配的記憶體,回收無用物件佔據的記憶體資源。但如果不是new得到的記憶體區域,就不能使用垃圾回收該記憶體,所以需要使用finalize()
  • 工作原理:一旦垃圾回收器準備好釋放物件佔用的儲存空間,將首先呼叫finalize()方法,並且在下一次垃圾回收動作發生時,才會真正回收物件佔用的記憶體。
  • 少使用的原因:finalize函式呼叫時,有可能導致物件復活;finalize函式執行的時間沒有保證;壞的finalize會影響gc效能,死鎖和執行緒掛起,還可能導致記憶體洩漏。

5.記憶體洩漏

記憶體洩漏就是存在一些被分配的物件,這些物件有兩個特點:1.物件是可達的,2.物件是無用的;這些物件不會被GC回收,但是佔用記憶體

引起的原因:

  • 靜態集合類:HashMap、Vector等的使用最容易出現記憶體洩漏,這些靜態變數的生命週期和應用程式一致,他們所引用的所有的物件Object也不能被釋放,因為他們也將一直被Vector等引用著
  • 當集合裡面的物件屬性被修改後,再呼叫remove()方法時不起作用。
  • 監聽器:在釋放物件的時候卻沒有刪除這些監聽器
  • 各種連線:資料庫連線、網路連線、io連線,除非顯示的呼叫了close()方法將其連線關閉,否則不會被GC回收
  • 內部類和外部模組的引用
  • 單例模式:不正確使用單例模式是引起記憶體洩漏的一個常見問題,單例物件在初始化後將在JVM的整個生命週期中存在(以靜態變數的方式),如果單例物件持有外部的引用,那麼這個物件將不能被JVM正常回收,導致記憶體洩漏。

6.java的四種引用

6.1強引用 Strong Reference

Object obj =  new  Object();

  程式碼中普遍存在的,像上述的引用。只要強引用還在,垃圾收集器永遠不會回收掉被引用的物件。

6.2軟引用 Soft Reference

如果一個物件具有軟引用,記憶體空間足夠,垃圾回收器就不會回收它;

如果記憶體空間不足了,就會回收這些物件的記憶體。只要垃圾回收器沒有回收它,該物件就可以被程式使用。

好處:

  • 軟引用可用來實現記憶體敏感的快取記憶體,比如網頁快取、圖片快取等。
  • 使用軟引用能防止記憶體洩露,增強程式的健壯性。   
  • SoftReference的特點是它的一個例項儲存對一個Java物件的軟引用, 該軟引用的存在不妨礙垃圾收集執行緒對該Java物件的回收。

6.3弱引用 Weak Reference

只能生存到下一次垃圾收集發生前。弱引用也是用來描述非必需物件的,當JVM進行垃圾回收時,無論記憶體是否充足,都會回收被弱引用關聯的物件。

6.4虛引用 Phantom Reference

  • 不影響物件的生命週期
  • 無法通過虛引用來取得一個物件例項
  • 為一個物件關聯虛引用的唯一目的,就是希望在這個物件被收集器回收時,收到一個系統通知

小結:物件可能不被垃圾回收;垃圾回收只與記憶體有關

參考:

相關推薦

JVM垃圾回收機制總結

JVM分為三個主要的子系統 (1)類載入器子系統(2)執行時資料區(3)執行引擎 1.類載入器子系統 Java的動態類載入功能是由類載入器子系統處理。當它在執行時(不是編譯時)首次引用一個類時,它載入、連結並初始化該類檔案。 1.1載入 類由此元件載入,遵循委託

JVM垃圾回收機制 總結(垃圾收集、回收算法、垃圾回收器)

策略 .html clas 高並發 hotspot 指定 %20 引用關系 新增  相信和小編一樣的程序猿們在日常工作或面試當中經常會遇到JVM的垃圾回收問題,有沒有在夜深人靜的時候詳細捋一捋JVM垃圾回收機制中的知識點呢?沒時間捋也沒關系,因為小編接下來會給你捋

Python垃圾回收機制 總結

限制 -i 統一 同時 over sans 指向 nor obb Python 垃圾回收機制內存管理Python中的內存管理機制的層次結構提供了4層,其中最底層則是C運行的malloc和free接口,往上的三層才是由Python實現並且維護的,第一層則是在第0層的基礎之上對

JVM垃圾回收機制

root 不一定 引用 不可達 tenured jvm 空間 大量 jvm垃圾回收 在java中是通過引用來和對象進行關聯的,也就是說如果要操作對象,必須通過引用來進行。那麽很顯然一個簡單的辦法就是通過引用計數來判斷一個對象是否可以被回收。不失一般性,如果一個對象沒有任何引

JVM垃圾回收機制演算法分析

JVM記憶體執行時資料區 一、什麼是垃圾回收機制gc垃圾回收機制&&演算法 什麼是垃圾回收機制: 不定時去堆記憶體清理不可達物件。不可達的物件並不會馬上就會直接回收,而是至少要經過兩次標記的過程。 public class Test { public st

JVM 垃圾回收機制與GC效能調優

一、GC概要: JVM堆相關知識     為什麼先說JVM堆?     JVM的堆是Java物件的活動空間,程式中的類的物件從中分配空間,其儲存著正在執行著的應用程式用到的所有物件。這些物件的建立方式就是那些new一類的操作,當物件

JVM系列第8講:JVM 垃圾回收機制

在第 6 講中我們說到 Java 虛擬機器的記憶體結構,提到了這部分的規範其實是由《Java 虛擬機器規範》指定的,每個 Java 虛擬機器可能都有不同的實現。其實涉及到 Java 虛擬機器的記憶體,就不得不談到 Java 虛擬機器的垃圾回收機制。因為記憶體總是有限的,我們需要一個機制來不斷地回收廢棄的記憶體

JVM——垃圾回收機制

文章目錄 判斷物件是否存活 引用計數法 可達性分析 垃圾收集演算法 標記—清除 複製演算法 標記—複製演算法 標記—整理演算法 分代收集演算法

Java:JVM垃圾回收機制

JVM垃圾回收機制 提到Java垃圾回收機制就不得不提到一個方法: system.gc() 用於呼叫垃圾收集器,在呼叫時垃圾收集器將執行以回收未使用的記憶體空間,它將嘗試釋放被丟棄物件所佔用的空間。 作為程式設計師有必要了解gc方

JVM垃圾回收機制入門

1. 先看Java的記憶體儲存結構 假設你是一個普通的 Java 物件,你出生在 Eden 區,在 Eden 區有許多和你差不多的小兄弟、小姐妹,可以把 Eden 區當成幼兒園,在這個幼兒園裡大家玩了很長時間。Eden 區不能無休止地放你們在裡面,所以當年紀稍大,你就要被送

3分鐘搞懂jvm垃圾回收機制

幾句話概括jvm垃圾回收機制 1. jvm在堆記憶體中分為新生代和老年代。 2. 新生代中分為eden區和from區、to區。新來的物件會放到eden區內。當eden區放不下了,會將eden區的物件一道from區。要是from區裝不下,就放到將from區裡的物件移到to區。如果to區記憶體也

(面試題)有關JVM垃圾回收機制的那些演算法

三種垃圾回收演算法 標記-清除(年老代) 標記-整理(即標記-壓縮)(年老代) 複製(年輕代) 1、標記-清除演算法   原理: 從根集合節點進行掃描,標記出所有的存活物件,最後掃描整個記憶體空間並清除沒有標記的物件(即死亡物件)

JVM 垃圾回收機制

引用類型 引用 垃圾收集器 內容 清理 才會 空閑 碎片 刪除 首先JVM的內存結構包括五大區域: 程序計數器、虛擬機棧、本地方法棧、方法區、堆區。其中程序計數器、虛擬機棧和本地方法棧3個區域隨線程啟動與銷毀, 因此這幾個區域的內存分配和回收都具有確定性,不需要過多考慮回收

深入理解JVM學習筆記(十九、JVM 垃圾回收機制---如何判斷物件是否為垃圾【引用計數法】)

一、引用計數法         引用計數演算法作為垃圾收集器最早的演算法,有其優勢,也有其劣勢,雖然現在的JVM都不再採用引用計數演算法進行垃圾回收【例如Sun的Java hotspot採用了火車演算法進行垃圾回收】,但這種演算法也並未被淘汰,在著名的單程序高併發快取Red

[深入理解JVM 七]---Jvm垃圾回收機制

本篇部落格大部分內容來自《深入理解java虛擬機器》,也參考了http://jbutton.iteye.com/blog/1569746這篇文章的部分內容,這裡註明出處。這篇部落格也是這個系列的第二篇,在這篇部落格裡我會對java的記憶體回收機制做個詳細的整理。

幾種JVM垃圾回收機制

1.1.標記-清除收集器 這種收集器首先遍歷物件圖並標記可到達的物件,然後掃描堆疊以尋找未標記物件並釋放它們的記憶體。這種收集器一般使用單執行緒工作並停止其他操作。 1.2.標記-壓縮收集器 有時也叫標記-清除-壓縮收集器,與標記-清除收集器有相同的標記階段。在第二階段,

JVM垃圾回收機制(一)

一、什麼是垃圾? 1:引用計數演算法:給物件中加一個引用計數器,每當有一個引用指向它時,計數器的值就加一,引用失效時,計數器的值就減一。當該物件引用計數器等於0的時候就被視為垃圾。 該演算法存在很大的缺陷,若兩個物件存在互相引用,則兩者的引用計數器都

深入理解JVM學習筆記(二十二、JVM 垃圾回收機制---如何回收垃圾---回收策略【複製演算法】)

        上一節我們講到了標記-清除演算法因為需要進行兩次記憶體掃描導致效率不高,那麼這一節我們介紹一種複製演算法,比較好的解決了這個問題。        講複製演算法前,我們先回顧一下JVM的記憶體結構。JVM記憶體大體分為兩大塊,分別為執行緒共享區、執行緒獨佔區。

JVM記憶體管理、JVM垃圾回收機制、新生代、老年代以及永久代

     如果大家想深入的瞭解JVM,可以讀讀周志明《深入理解Java虛擬機器:JVM高階特性與最佳實踐》      需要掌握的東西,包括以下內容、判斷物件存活還是死亡的演算法(引用計數演算法、可達性分析演算法)、常見的垃圾收集演算法(複製演算法、分代收集演算法等以及這

Java虛擬機器的JVM垃圾回收機制

1.JVM記憶體空間   JVM堆(Heap)= 新生代(Young) + 舊生代(Tenured)  分割槽作用:  新建立的物件通常先將其分配在新生代中,在新生代中經過若干次GC之後仍未釋放的物件,再將它移動到舊生代。為了讓記憶體回收更高效(GC會暫停JVM中的應用