JVM垃圾回收的原理是什么

這期內(nèi)容當(dāng)中小編將會(huì)給大家?guī)碛嘘P(guān)JVM垃圾回收的原理是什么,文章內(nèi)容豐富且以專業(yè)的角度為大家分析和敘述,閱讀完這篇文章希望大家可以有所收獲。

創(chuàng)新互聯(lián)是一家業(yè)務(wù)范圍包括IDC托管業(yè)務(wù),網(wǎng)站空間、主機(jī)租用、主機(jī)托管,四川、重慶、廣東電信服務(wù)器租用,服務(wù)器主機(jī)托管,成都網(wǎng)通服務(wù)器托管,成都服務(wù)器租用,業(yè)務(wù)范圍遍及中國大陸、港澳臺(tái)以及歐美等多個(gè)國家及地區(qū)的互聯(lián)網(wǎng)數(shù)據(jù)服務(wù)公司。

概述

Java運(yùn)行時(shí)區(qū)域中,程序計(jì)數(shù)器,虛擬機(jī)棧,本地方法棧三個(gè)區(qū)域隨著線程的而生,隨線程而死,這幾個(gè)區(qū)域的內(nèi)存分配和回收都具備確定性,不需要過多考慮回收問題。而Java堆和方法區(qū)則不一樣,一個(gè)接口的多個(gè)實(shí)現(xiàn)類需要的內(nèi)存不一樣,一個(gè)方法的多個(gè)分支需要的內(nèi)存可能也不一眼,我們只有在運(yùn)行期,才能知道會(huì)創(chuàng)建的對(duì)象,這部分的內(nèi)存分配和回收,是垃圾回收器所關(guān)注的。垃圾回收器需要完成三個(gè)問題:那些內(nèi)存需要回收;什么時(shí)候回收以及如何回收。

那些垃圾需要回收

垃圾回收的基本思想是考察一個(gè)對(duì)象的可達(dá)性,即從根節(jié)點(diǎn)開始是否可以訪問到這個(gè)對(duì)象,如果可以,則說明對(duì)象正在被使用,相反如果從根節(jié)點(diǎn)無法訪問到這個(gè)對(duì)象,說明對(duì)象已經(jīng)不再使用了,一般來說此對(duì)象就是需要被回收的。這個(gè)算法為根搜索算法。

可達(dá)性分析

但是實(shí)際中,一個(gè)不可達(dá)的對(duì)象有可能在某種條件下“復(fù)活”自己,那么對(duì)它的回收就是不合理的。為此給出一個(gè)對(duì)象可達(dá)性狀態(tài)的定義,并規(guī)定了在什么狀態(tài)下可以安全的回收對(duì)象??蛇_(dá)性對(duì)象包含了以下三種狀態(tài)。

可達(dá)的:從根節(jié)點(diǎn)開始,按照引用節(jié)點(diǎn),可以搜索到這個(gè)對(duì)象

可復(fù)活的:對(duì)象的所有引用都被釋放,但是對(duì)象可能在finalize()方法中復(fù)活自己。

不可達(dá)的:對(duì)象的finalize()方法被調(diào)用,并且沒有復(fù)活,那么就進(jìn)入不可達(dá)狀態(tài)。不可達(dá)的對(duì)象不可能會(huì)被“復(fù)活”,因?yàn)閒inalize()方法只能調(diào)用一次。

/**
 * 
 * <p>Description: 1.對(duì)象被GC時(shí),可以通過finalize拯救 2.finalize只被調(diào)用一次 </p> 
 * @date 2019年8月25日 
 * @version 1.0
 */
public class FinalizeTest {

  private static FinalizeTest currentObj;

  @Override
  protected void finalize() throws Throwable {
    super.finalize();
    System.out.println("finalize invoke");
    //重新引用
    currentObj = this;
  }

  public void alive() {
    System.out.println("live");
  }

  public static void main(String[] args) throws InterruptedException {
    currentObj = new FinalizeTest();

    currentObj = null;
    System.gc();
    //finalize優(yōu)先級(jí)地,先等待
    Thread.sleep(500);
    if(currentObj == null) {
      System.out.println("dead");
    }else {
      currentObj.alive();
    }

    currentObj = null;
    System.gc();
    //finalize優(yōu)先級(jí)地,先等待
    Thread.sleep(500);
    if(currentObj == null) {
      System.out.println("dead");
    }else {
      currentObj.alive();
    }
  }
}

上面代碼有一處一樣的斷碼片段,但是得到的結(jié)果卻并不相同,一次對(duì)象“拯救復(fù)活”成功,另一次失敗,那么就可以被正常回收。

可以作為GC Roots包括下面幾種:

  • 虛擬機(jī)棧(棧幀中的本地表量表)中引用的對(duì)象

  • 方法區(qū)中類靜態(tài)屬性引用的對(duì)象

  • 方法區(qū)中常量引用的對(duì)象

  • 本地方法棧中JNI引用(即一般Native的方法)的對(duì)象

四種引用類型

在JDK1.2之后對(duì)引用進(jìn)行了擴(kuò)充,分為強(qiáng)引用,軟引用,弱引用,虛引用4種,這四種強(qiáng)度一次減弱。通過對(duì)引用的擴(kuò)充,可以依據(jù)內(nèi)存的使用來描述這樣的對(duì)象:當(dāng)內(nèi)存足夠,則保留內(nèi)存中;如果內(nèi)存空間進(jìn)行垃圾回收后還是很緊張,則可以拋棄這類對(duì)象。很多系統(tǒng)的緩存功能符合這樣的應(yīng)用場景。

強(qiáng)引用

在Java中最常見的就是強(qiáng)引用, 把一個(gè)對(duì)象賦給一個(gè)引用變量,這個(gè)引用變量就是一個(gè)強(qiáng)引用。當(dāng)一個(gè)對(duì)象被強(qiáng)引用變量引用時(shí),它處于可達(dá)狀態(tài),它是不可能被垃圾回收機(jī)制回收的,即使該對(duì)象以后永遠(yuǎn)都不會(huì)被用到JVM也不會(huì)回收。因此強(qiáng)引用是造成Java內(nèi)存泄漏的主要原因之一。

軟引用

軟引用需要用SoftReference類來實(shí)現(xiàn),對(duì)于只有軟引用的對(duì)象來說,當(dāng)系統(tǒng)內(nèi)存足夠時(shí)它不會(huì)被回收,當(dāng)系統(tǒng)內(nèi)存空間不足時(shí)它會(huì)被回收。軟引用通常用在對(duì)內(nèi)存敏感的程序中。

弱引用

弱引用需要用WeakReference類來實(shí)現(xiàn),它比軟引用的生存期更短,對(duì)于只有弱引用的對(duì)象來說,只要垃圾回收機(jī)制一運(yùn)行,不管 JVM 的內(nèi)存空間是否足夠,總會(huì)回收該對(duì)象占用的內(nèi)存。

虛引用

虛引用需要PhantomReference類來實(shí)現(xiàn),它不能單獨(dú)使用,必須和引用隊(duì)列聯(lián)合使用。 虛引用的主要作用是跟蹤對(duì)象被垃圾回收的狀態(tài)。

什時(shí)候回收

按HotSpot VM的serial GC的實(shí)現(xiàn)來看觸發(fā)條件主要分為以下幾種:

  • young GC:當(dāng)young gen中的eden區(qū)分配滿的時(shí)候觸發(fā)。注意young GC中有部分存活對(duì)象會(huì)晉升到old gen,所以young GC后old gen的占用量通常會(huì)有所升高。

  • full GC:當(dāng)準(zhǔn)備要觸發(fā)一次young GC時(shí),如果發(fā)現(xiàn)統(tǒng)計(jì)數(shù)據(jù)說之前young GC的平均晉升大小比目前old gen剩余的空間大,則不會(huì)觸發(fā)young GC而是轉(zhuǎn)為觸發(fā)full GC(因?yàn)镠otSpot VM的GC里,除了CMS的concurrent collection之外,其它能收集old gen的GC都會(huì)同時(shí)收集整個(gè)GC堆,包括young gen,所以不需要事先觸發(fā)一次單獨(dú)的young GC);或者,如果有perm gen的話,要在perm gen分配空間但已經(jīng)沒有足夠空間時(shí),也要觸發(fā)一次full GC;或者System.gc()、heap dump帶GC,默認(rèn)也是觸發(fā)full GC。

HotSpot VM里其它非并發(fā)GC的觸發(fā)條件復(fù)雜一些,不過大致的原理與上面說的其實(shí)一樣。并發(fā)GC的觸發(fā)條件就不太一樣。以CMS GC為例,它主要是定時(shí)去檢查old gen的使用量,當(dāng)使用量超過了觸發(fā)比例就會(huì)啟動(dòng)一次CMS GC,對(duì)old gen做并發(fā)收集。

如何回收

如何回收主要就涉及到垃圾回收的算法了。下面介紹幾種垃圾回收算法的思想。

標(biāo)記清除法(Mark-Sweep)

標(biāo)記清除算法是現(xiàn)代垃圾回收算法的思想基礎(chǔ)。它主要分為兩個(gè)階段:標(biāo)記階段和清除階段。在標(biāo)記階段,首先通過根節(jié)點(diǎn),標(biāo)記所有從根節(jié)點(diǎn)開始的可達(dá)隊(duì)對(duì)象,因此未被標(biāo)記的對(duì)象就是未被引用的垃圾對(duì)象。然后在清除階段,清除所有的未被標(biāo)記的對(duì)象。

標(biāo)記清除算法的不足有:效率的問題和標(biāo)記清除后產(chǎn)生的大量不連續(xù)的內(nèi)存碎片。而內(nèi)存碎片太多可能會(huì)導(dǎo)致在分配大對(duì)象時(shí),無法找到連續(xù)的內(nèi)存而不得不提前觸發(fā)另外一次垃圾回收。

復(fù)制算法(Coping)

復(fù)制算法的核心思想是:將原有的內(nèi)存空間分為兩塊,每次只使用其中一塊,在垃圾回收時(shí),將正在使用的內(nèi)存中存活對(duì)象復(fù)制到未使用的內(nèi)存塊中,之后清除正在使用的內(nèi)存塊中的所有對(duì)象,交換兩個(gè)內(nèi)存的角色,完成垃圾回收。

如果系統(tǒng)中的待回收的對(duì)象很多,復(fù)制算法需要復(fù)制的存活對(duì)象就會(huì)相對(duì)較少,真正的垃圾回收時(shí)刻,復(fù)制算法的效率就會(huì)很高。而且對(duì)象是在垃圾回收過程中的,統(tǒng)一復(fù)制到新的內(nèi)存空間,再清除原來使用的內(nèi)存,因此可以確?;厥蘸蟮膬?nèi)存空間是沒有碎片的。但是另一方面,復(fù)制算法的代價(jià)是需要使用更多的內(nèi)存空間。

復(fù)制算法比較適用于新生代。因?yàn)樾律鴮?duì)象通常多余存活對(duì)象,復(fù)制算法的效率會(huì)比較高。

標(biāo)記整理算法(Mark Compact)

在老年代,大部分的對(duì)象都是存活對(duì)象。如果依然用復(fù)制算法,由于存活的對(duì)象多,復(fù)制的成本也將提高。因此基于老年代的垃圾回收特性,需要使用其他的算法。標(biāo)記整理算法是一種老年代的回收算法。它在標(biāo)記算法的基礎(chǔ)上做了一些優(yōu)化。和標(biāo)記清除算法一樣,它也是從更節(jié)點(diǎn)開始,但是并不是清除未標(biāo)記的對(duì)象,而是將存活的對(duì)象壓縮到內(nèi)存的一邊,之后清除邊界外所有空間。這種方法避免了碎片的產(chǎn)生,又不需要過多的內(nèi)存空間,因此性價(jià)比比較高。

標(biāo)記整理法的最終效果等同于標(biāo)記清除算法執(zhí)行完成后,再進(jìn)行一次內(nèi)存碎片的整理,因此也可以把它稱為標(biāo)記清除整理(MarkSweepComact)。

分代算法(Generational Collecting)

分代算法是根據(jù)對(duì)象存活周期不同將內(nèi)存化為幾塊。一般是把Java堆分為新生代和老年代,這樣就可以根據(jù)各個(gè)年代的特點(diǎn)采用最合適的收集算法。新生代中的特點(diǎn)是對(duì)象朝生夕死,大約90%的新建對(duì)象會(huì)被回收,因此新生代適合復(fù)制算法。當(dāng)一個(gè)對(duì)象經(jīng)過幾次回收后依然存活,對(duì)象就會(huì)被放入老年代的內(nèi)存空間。在老年代中可以認(rèn)為對(duì)象在一段時(shí)間內(nèi),甚至在程序的整個(gè)生命周期,是常駐內(nèi)存的,可以對(duì)老年代使用標(biāo)記清除和標(biāo)記整理算法。

對(duì)于新生代和老年代來說,通常新生代的回收頻率很高,但是每次回收的耗時(shí)都很短,而老年代回收的頻率比較低,但是會(huì)消耗更多的時(shí)間。

分區(qū)算法(Region)

一般來說,相同條件下,堆空間越大,一次GC所需要的事件越長,從而產(chǎn)生的停頓也越長。為了更好的靠之停頓時(shí)間,將一塊大的內(nèi)存區(qū)域分割成多個(gè)大小形同的小區(qū)域,依據(jù)目標(biāo)的停頓時(shí)間,每次回收若干個(gè)小區(qū)間,而不是整個(gè)堆空間,從而減少一次GC所產(chǎn)生的停頓。分區(qū)算法是將整個(gè)堆空間劃分為連續(xù)的不同小區(qū)間。每個(gè)小區(qū)間獨(dú)立使用,獨(dú)立回收。

上述就是小編為大家分享的JVM垃圾回收的原理是什么了,如果剛好有類似的疑惑,不妨參照上述分析進(jìn)行理解。如果想知道更多相關(guān)知識(shí),歡迎關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道。

網(wǎng)站名稱:JVM垃圾回收的原理是什么
分享網(wǎng)址:http://muchs.cn/article30/ihseso.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供營銷型網(wǎng)站建設(shè)、網(wǎng)站改版、做網(wǎng)站Google、云服務(wù)器域名注冊(cè)

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場,如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來源: 創(chuàng)新互聯(lián)

小程序開發(fā)