溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

Java虛擬機(jī)的內(nèi)存管理方式

發(fā)布時(shí)間:2021-08-24 17:35:06 來源:億速云 閱讀:90 作者:chen 欄目:云計(jì)算

這篇文章主要講解了“Java虛擬機(jī)的內(nèi)存管理方式”,文中的講解內(nèi)容簡(jiǎn)單清晰,易于學(xué)習(xí)與理解,下面請(qǐng)大家跟著小編的思路慢慢深入,一起來研究和學(xué)習(xí)“Java虛擬機(jī)的內(nèi)存管理方式”吧!

   一說到內(nèi)存管理,首先需要了解它的內(nèi)存模型。   

 虛擬機(jī)的內(nèi)存模型在jdk1.8之后有了一些變化,我們分開來看,請(qǐng)看下圖:

   

Java虛擬機(jī)的內(nèi)存管理方式

   

由圖我們可以看出,jdk每個(gè)版本都會(huì)有新生代和老年代,唯一不同的是小于1.8的版本為永久代,而大于等于1.8的版本去掉了永久代,轉(zhuǎn)為元空間(Meta Space)。

   

永久代也就是存儲(chǔ)的數(shù)據(jù)區(qū)里面的方法區(qū),如果程序在運(yùn)行中發(fā)生PermSpace溢出,則說明永久代內(nèi)存不夠,需要調(diào)整JVM參數(shù)增加永久代內(nèi)存空間。

   

jdk1.8以后出現(xiàn)了MetaSpace,它和永久代不同的是,它的內(nèi)存空間是動(dòng)態(tài)擴(kuò)展的,當(dāng)然我們也可以設(shè)置MaxMetadaSpace來設(shè)置最大元空間內(nèi)存數(shù)量,也就是在1.8以后設(shè)置PermSize是無效的。

   

本文主要講解jdk1.8以前得內(nèi)存管理機(jī)制

 

在現(xiàn)代編程語言中,對(duì)垃圾回收算法主要有兩種方式:引用計(jì)數(shù)器和可達(dá)性分析。

引用計(jì)數(shù)器

引用計(jì)數(shù)的原理大致是這樣的:為每一個(gè)創(chuàng)建的對(duì)象設(shè)置一個(gè)引用計(jì)數(shù),每當(dāng)對(duì)象被引用一次后,引用計(jì)數(shù)加1,當(dāng)對(duì)象引用失效時(shí),引用計(jì)數(shù)減1,當(dāng)引用計(jì)數(shù)為0是說明沒有任何對(duì)象引用了,即可釋放該對(duì)象。

引用計(jì)數(shù)的一個(gè)弊端是,他無法解決對(duì)象間相互引用的問題,比如下面這段代碼:

public class RefrenceCountingGC {

    private Object instance = null;

    public static void main(String[] args) {
        RefrenceCountingGC gc1 = new RefrenceCountingGC();
        RefrenceCountingGC gc2 = new RefrenceCountingGC();
        gc1.instance = gc2;
        gc2.instance = gc1;

        gc1 = null;
        gc2 = null;
        
        System.gc();
    }
}
 

兩個(gè)對(duì)象始終處于相互引用階段,因?yàn)橐糜?jì)數(shù)永遠(yuǎn)無法為0,因此就不能自動(dòng)釋放它。

可達(dá)性分析

 

為了解決引用計(jì)數(shù)出現(xiàn)的這些問題,可達(dá)性分析算法出現(xiàn)了。

   

在主流的編程語言中,java和c#都是通過可達(dá)性分析來判定對(duì)象是否存活的。

   

它的原理大致是:通過一系列的被稱為“GC Roots”的對(duì)象作為起始點(diǎn),然后從這些節(jié)點(diǎn)開始向下搜索,搜索的路徑連成的一條線,我們稱之為“引用鏈”,當(dāng)前一個(gè)對(duì)象到GC Roots沒有任何引用鏈,即我們說的這個(gè)對(duì)象不可達(dá)時(shí),則說明該對(duì)象是可以被回收的,通過下圖可以更好的理解:
 Java虛擬機(jī)的內(nèi)存管理方式

   

當(dāng)一個(gè)對(duì)象不可達(dá)時(shí),并不能代碼這個(gè)對(duì)象就能馬上被回收,他會(huì)處于死緩狀態(tài),而一個(gè)對(duì)象真正要回收時(shí),至少需要經(jīng)歷兩次標(biāo)記。第一次標(biāo)記的前提條件是,看該對(duì)象是否覆蓋了finalize()方法或者finalize()方法被虛擬機(jī)調(diào)用過,如果覆蓋了finalize()方法并且虛擬機(jī)還沒有調(diào)用過,這時(shí)會(huì)標(biāo)記它,該對(duì)象還有機(jī)會(huì)存活,方法很多,比如在finalize()方法內(nèi)存引用該對(duì)象。

   

如果進(jìn)行第二次回收時(shí),由于虛擬機(jī)已經(jīng)調(diào)用過finalize()方法,就不會(huì)再調(diào)用他了,這時(shí)該對(duì)象就會(huì)真正宣告死亡了。

   

請(qǐng)看下面這段代碼:

public class GCRoot {

    private static GCRoot instance = null;

    @Override
    protected void finalize() throws Throwable {
        super.finalize();
        System.out.println("finalized執(zhí)行");
        instance = this;
    }

    public static void main(String[] args) throws Exception{
        instance = new GCRoot();
        instance = null;
        System.gc();
        
        Thread.sleep(500);
        if(null != instance){
            System.out.println("對(duì)象拯救成功!");
        }else{
            System.out.println("對(duì)象被釋放!");
        }

        
        instance = null;
        System.gc();
        if(null != instance){
            System.out.println("對(duì)象拯救成功!");
        }else{
            System.out.println("對(duì)象被釋放!");
        }
    }
}
運(yùn)行結(jié)果:
finalized執(zhí)行
對(duì)象拯救成功!
對(duì)象被釋放!

java引用

   jdk1.2之前的引用很簡(jiǎn)單,這里我們不探討,我們主要探討jdk1.2之后的引用。  
   

java中將引用分為了:強(qiáng)引用、軟引用、弱引用和虛引用。

強(qiáng)引用

強(qiáng)引用在java程序中最常見的一種引用類型,類似Object o = new Object()這類引用,只要強(qiáng)引用還在,垃圾回收器就永遠(yuǎn)不會(huì)回收它。

軟引用

軟引用通常用來描述一些可以用但非必須的對(duì)象,在內(nèi)存溢出之前會(huì)先回收掉軟引用相關(guān)聯(lián)的對(duì)象,如果回收后內(nèi)存依然不夠,則才會(huì)拋出內(nèi)存溢出異常。

弱引用

弱引用用來描述一些非必須的對(duì)象,但是它的強(qiáng)度比軟引用還有弱一些。被弱引用關(guān)聯(lián)的對(duì)象只能存活到下次垃圾回收器工作之前,當(dāng)垃圾回收器開始工作時(shí),無論當(dāng)前內(nèi)存是否足夠,都會(huì)回收掉被弱引用關(guān)聯(lián)的對(duì)象。

虛引用

虛引用是最弱的一種引用類型,為對(duì)像設(shè)置虛引用關(guān)系的唯一目的就是能在這個(gè)對(duì)象被垃圾回收之時(shí)收到一個(gè)系統(tǒng)通知。

1、標(biāo)記-清除算法

 

這是最基礎(chǔ)的一種垃圾收集算法,后續(xù)所有的算法都是在這個(gè)算法的基礎(chǔ)上進(jìn)行擴(kuò)展。

   

通過算法的名字大致能夠看出,該算法分為了“標(biāo)記”和“清除”兩個(gè)階段:首先需要標(biāo)記出需要回收的所有對(duì)象,待標(biāo)記完成后清除掉所有標(biāo)記過的對(duì)象。這個(gè)算法的不足主要有兩個(gè):一是性能問題,標(biāo)記和清除兩個(gè)階段的性能都不高,二是標(biāo)記清除后會(huì)產(chǎn)品不連續(xù)的大量?jī)?nèi)存碎片,內(nèi)存碎片太多會(huì)導(dǎo)致下一次在需要分配占用大量?jī)?nèi)存的對(duì)象時(shí),無法找到足夠的連續(xù)碎片而不得不再一次觸發(fā)垃圾收集動(dòng)作。

2、復(fù)制算法

 

為了解決效率問題,復(fù)制算法出現(xiàn)了。這種算法會(huì)將可用內(nèi)存區(qū)域劃分為大小相同的兩塊,當(dāng)需要垃圾回收時(shí),會(huì)先將可用的對(duì)象復(fù)制到另一個(gè)內(nèi)存區(qū)域,從而將當(dāng)前區(qū)域一次性清除。這樣做的好處是每次都將一整塊內(nèi)存區(qū)域清除掉,從而避免了大量的內(nèi)存碎片出現(xiàn)。

   

目前主流的商用虛擬機(jī)大多是采用復(fù)制算法來回收新生代。

3、標(biāo)記-整理算法

 

當(dāng)對(duì)象的存活率較高時(shí)采用復(fù)制算法,效率就會(huì)很低,因此對(duì)于老年代一般不采用復(fù)制算法。

   

鑒于這種問題,一種稱之為“標(biāo)記-整理”算法的思路出現(xiàn)了。它和“標(biāo)記-清除”算法一樣,都需要先進(jìn)行標(biāo)記,但是它不會(huì)簡(jiǎn)單一次性清除標(biāo)記的對(duì)象,而是將所有存活對(duì)象都移動(dòng)到另一端,然后清除掉邊界外的對(duì)象。

4、分代收集算法

 

分代收集其實(shí)就是將內(nèi)存劃分為幾塊,比如將java堆劃分為新生代和老年代,根據(jù)不用年代采取最適合的算法,所以目前主流商用虛擬機(jī)都是采用這種方式。

感謝各位的閱讀,以上就是“Java虛擬機(jī)的內(nèi)存管理方式”的內(nèi)容了,經(jīng)過本文的學(xué)習(xí)后,相信大家對(duì)Java虛擬機(jī)的內(nèi)存管理方式這一問題有了更深刻的體會(huì),具體使用情況還需要大家實(shí)踐驗(yàn)證。這里是億速云,小編將為大家推送更多相關(guān)知識(shí)點(diǎn)的文章,歡迎關(guān)注!

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI