成人无码视频,亚洲精品久久久久av无码,午夜精品久久久久久毛片,亚洲 中文字幕 日韩 无码

資訊專欄INFORMATION COLUMN

JVM 對象引用標記 與 內(nèi)存回收算法

StonePanda / 2545人閱讀

摘要:就是說引用計數(shù)法很難解決對象之間的相互引用問題也無法通知回收器去及時回收它。因為存在這種缺點,所以現(xiàn)在的虛擬機基本上并不是通過引用計數(shù)法來判斷對象是否存活。

1、為什么要進行垃圾回收?

每當在我們寫代碼的時候,不管是new一個對象,還是引用,還是填充數(shù)據(jù)到數(shù)組,都是要占用空間,那么如果不及時回收就會對系統(tǒng)的運行產(chǎn)生影響。java和c 一個很大的區(qū)別就在于,java的垃圾回收主要是jvm去做,而c語言是自己去控制。雖然JAVA可以手動的調(diào)用方法 system.gc 去手動控制垃圾回收,但據(jù)說達不到立馬回收的效果。c 語言則是要自己去申請一塊內(nèi)存空間malloc ,使用完成還需要手動去釋放掉,如果沒有及時釋放,或者申請出現(xiàn)內(nèi)存過大等,會造成內(nèi)存溢出等異常,不過功底深厚的大牛都會做的比較牛逼,很好的去控制。

2、如何判斷對象生死?

在內(nèi)存回收的過程中,首先要確定一點,該對象是否應該被回收,哪些還"存活",哪些已"死亡"。

引用計數(shù)法

當一個對象在代碼中被引用,那么就會在這個對象的引用計數(shù)值 + 1,當引用失效的時候,計數(shù)值則相應 - 1。但是如果兩個對象存在相互引用的情況,如下:

    A a = new A(); 
    B b = new B();
    a.instance = b;
    b.instance = a;

虛擬機就無法去判斷這兩個對象是否需要被回收,因為彼此的引用值都不是 0。就是說引用計數(shù)法很難解決對象之間的相互引用問題, 也無法通知GC回收器去及時回收它。因為存在這種缺點,所以現(xiàn)在的虛擬機基本上并不是通過引用計數(shù)法來判斷對象是否存活。那是通過什么方法? 請看官往下看。

可達性分析算法

現(xiàn)在主流的商用語言的視線中都是通過可達性分析來判斷對象是否存活,比如JAVA,C#等。這種方法基本思想 ——以 GC Roots的對象作為起點向下搜索,搜索走過的路徑被稱為"引用鏈",當一個對象沒有任何引用鏈相連,那么這個對象就是不可用的。如下圖所示:

gc roots 是什么? 是滿足下面任意條件的某個對象。

虛擬機棧中reference對象;

方法區(qū)靜態(tài)屬性引用對象;

方法區(qū)常量引用對象;

本地方法棧 所謂的native方法 引用的對象。

Hotspot中的native方法引用Java對象用的是通過句柄(handle)來引用。HotSpot的JNI handle是放在若干不同的區(qū)域里的,但不會放在GC堆中。傳遞參數(shù)用的handle直接在棧上;local handle放在每個Java線程中的JNIHandleBlock里;global handle放在VM全局的JNIHandleBlock里。
關(guān)于什么是native方法請如下鏈接:https://segmentfault.com/n/13...

注:并不是不可達的對象就必須 "死",他們還是處于"緩刑", 真正要宣告一個對象死亡,需要經(jīng)過兩次標記的過程:經(jīng)過可達性分析后對象沒有和GC Roots 連接的引用鏈,那么需要被標記一次然后還需要經(jīng)過篩選(篩選條件:判斷該對象是否有必要執(zhí)行finalize()方法),如果對象已經(jīng)調(diào)用了或者沒有覆蓋finalize方法(finalize() 方法只會被執(zhí)行一次!),那么 虛擬機判定該對象是 "沒有必要執(zhí)行該方法"。

如果該對象有必要執(zhí)行finalize方法,那么對象會被放置在一個叫做F-Queue 的隊列之中,之后會由虛擬機自動建立,由低優(yōu)先級的Finalize 方法去執(zhí)行。(執(zhí)行時只去觸發(fā)對象的finalize()方法,但是并不等待他運行結(jié)束,防止有的對象finalize()進行緩慢,或者死循環(huán),會導致隊列持續(xù)等待,進而內(nèi)存回收系統(tǒng)崩潰。)稍后GC 會對F-Queue 隊列中的對象進行第二次標記,當finalize 方法執(zhí)行后成功將對象連接到引用鏈上任何一個對象,那么這個對象就被拯救成功了,不然則go die!

什么是引用?

Java中定義:如果reference類型的數(shù)據(jù)中存儲的數(shù)值代表的是另一塊內(nèi)存的起始地址,就稱是這塊內(nèi)存的一個引用。

強引用:類似于A a = new A(),只要引用在,就永遠不會回收被引用的對象。

軟引用:描述有用但并非必須的獨享。在系統(tǒng)將要發(fā)生內(nèi)存溢出的時候,會將這部分對象回收。

弱引用:非必需對象引用,能生存到下次發(fā)生GC之前。

虛引用:一點用都沒。只有當對象被垃圾回收器回收的時候會收到一個系統(tǒng)通知。

方法區(qū)回收

在我看來方法區(qū)的內(nèi)存,回收起來并沒有新生代那么明顯。方法區(qū)大多存有類的描述信息,靜態(tài)變量,常量,方法等信息,這些大多是系統(tǒng)常用的,很少去回收,回收效率微乎其微。然而永久帶的方法回收主要分成兩部分,一種是常量的回收,另一種是類的回收。

常量的回收

相當于這個常量已經(jīng)被廢棄掉了。例如:方法區(qū)的常量池中有一個字符串常量 "java", 當系統(tǒng)中沒有一個String對象指向這個常量的值得時候,那么這個常量在發(fā)生GC的時候?qū)换厥铡?br>類的回收

類的回收

相對于常量的回收會麻煩多,需要滿足下面三個條件才會被回收:
1、類中所有的對象都被回收,就是堆中不存在該類的任何的實例;
2、加載該類的classloader被回收;
3、該類對應的java.lang.Class對象沒有在任何地方引用,或是通過反射機制訪問不到該類。

注: 在大量使用反射,動態(tài)代理,cGLib等ByteCode框架、動態(tài)生成Jsp等頻繁定義classLoader的場景都需要虛擬機具備類的卸載功能,防止永久帶不會溢出。

3、垃圾回收算法

標記-清除算法

標記-清除 算法是最基礎的算法,為什么呢?因為后面的要講的算法很多是從這個基本的算法改變其不足演變而來。

標記-清除(Mark-Sweep) 算法正如其名字所說由兩個部分來完成。首先,要對需要回收的對象進行標記,如何標記上面已經(jīng)提過。然后,要對這些被標記的對象進行收集。

標記:標記的過程其實就是,遍歷所有的GC Roots,然后將所有GC Roots可達的對象標記為存活的對象。
清除:清除的過程將遍歷堆中所有的對象,將沒有標記的對象全部清除掉。

如下圖所示:

感覺清理完成之后,內(nèi)存零零散散,故該算法有以下兩個缺點:
缺點一:被標記的對象在內(nèi)存中分布很零散,回收之后可用內(nèi)存很零碎。如果當一個進程需要申請一塊連續(xù)的較大內(nèi)存時,無法找到足夠的連續(xù)內(nèi)存,不得不提前觸發(fā)一次垃圾回收的動作。
缺點二標記和清除的過程效率不高,而且在進行GC的時候,需要停止應用程序,這會導致用戶體驗非常差,尤其對于交互式的應用程序來說簡直是無法接受。

復制算法

復制算法是對標記-清除算法在回收后出現(xiàn)很多內(nèi)存碎片的一種改進,而且效率也有所提升。

復制算法(copying)將可用的內(nèi)存容量劃分成大小相等兩塊,每次只使用其中一塊,當一塊內(nèi)存用完了,將還存活的對象復制到另一塊內(nèi)存中,然后將之前使用過的內(nèi)存空間全部清理掉。當有效內(nèi)存空間耗盡時,JVM將暫停程序運行,開啟復制算法GC線程 ,它會將活動區(qū)間內(nèi)的存活對象,全部復制到空閑區(qū)間,且嚴格按照內(nèi)存地址依次排列,與此同時,GC線程將更新存活對象的內(nèi)存引用地址指向新的內(nèi)存地址。 清空之前的內(nèi)存塊,減少了大量不連續(xù)的內(nèi)存碎片的產(chǎn)生,實現(xiàn)簡單且運行高效。

如下圖所示:

細心的讀者會發(fā)現(xiàn),這種算法有個很大的缺點——將可使用的內(nèi)存縮小成原來的一半,代價太大了!所以現(xiàn)在的虛擬機廠商都采用復制算法來回收新生代。研究表明 新生代對象98%都是 “朝生夕死” 所以不需要按照1:1劃分內(nèi)存空間,而是將內(nèi)存分為一塊較大的Eden 空間和兩塊較小的Survivor 空間。每次只是用 Eden 和 其中一塊 Survivor區(qū)域,另一塊Survivor 則用來當作保留區(qū)。 那么這樣一來,每次進行回收的時候只需要將Eden 和 Survivor生還的對象復制到另一塊 Survivor空間,然后清理。HotShot虛擬機 新生代劃分比例默認 Eden:Survivor = 8:1。然而這樣分配內(nèi)存,有個問題。當作保留區(qū)的Survivor的內(nèi)存大小不夠承載 使用中的Eden和一塊Survivor區(qū)域的存活對象怎么辦?此時需要依賴其他內(nèi)存(老年代)進行分配擔保。

分配擔保(Handle Promotion)——如果另一塊Survivor空間沒有足夠的空間存放上一次新生代收集下來的存活對象時,這些對象直接通過分配擔保機制進入老年代。

發(fā)生Minor GC(只回收Eden和Survivor區(qū))前,虛擬機檢查老年代最大可用連續(xù)空間是否大于新生代所有對象總空間。如果大于,那么Minor GC確保是安全的。如果不大于,則需要查看虛擬機HandlePromotionFailure參數(shù)設置,是否允許擔保失敗。若允許(true),會繼續(xù)檢查老年代最大連續(xù)可用空間是是否大于歷次晉升到老年代的對象平均大小。如果大于,會嘗試一次 Minor GC,盡管是有風險。(因為僅僅是歷次晉升到老年代對象平均大小與老生代最大連續(xù)空間比較,如果內(nèi)存小無法容納,此時進行Minor GC 會清理原本存活的對象所以是冒險的,進而需要進行Full GC)如果小于或者Handle Promotion Failure不允許冒險,那么要進行一次Full GC。

在jdk1.6 update24以后handle promotion failure 參數(shù)已經(jīng)不會影響到分配擔保的判定,具體代碼如下:

bool TenuredGeneration::promotion_attempt_is_safe(size_t  
max_promotion_in_bytes) const   
{  
   // 老年代最大可用的連續(xù)空間  
   size_t available = max_contiguous_available();    
   // 每次晉升到老年代的平均大小  
   size_t av_promo  = (size_t)gc_stats()->avg_promoted()->padded_average();  
   // 老年代可用空間是否大于平均晉升大小,或者老年代可用空間是否大于當此GC時新生代所有對象容量  
   bool   res = (available >= av_promo) || (available >=max_promotion_in_bytes);  
   return res;  
}

如果老年代連續(xù)空閑空間大于歷屆晉升到老年代的對象的平均空間可以直接minor GC 否則 Full GC。

標記-整理算法

復制算法是需要將對象從從內(nèi)存一個區(qū)域復制到另一個區(qū)域,當發(fā)現(xiàn)對象存活率很高的情況下,效率很低。而且在老生代的回收中,大多不采用復制算法,沒有額外的空間進行分配擔保。

標記-整理算法(Mark-Compact),過程和Mark-Sweep 方法過程一樣,也需要對對象進行標記,不過后續(xù)步驟不是直接對可回收對象進行清理,算法分成兩個部分?! ?/p>

標記:遍歷GC Roots,然后將存活的對象標記。
整理:移動所有存活的對象,且按照內(nèi)存地址次序依次排列,然后將末端內(nèi)存地址以后的內(nèi)存全部回收。

標記-整理算法不僅可以彌補標記-清除算法當中,內(nèi)存區(qū)域分散的缺點,也消除了復制算法當中,內(nèi)存減半的高額代價。不過標記-整理算法,效率是唯一缺點。它需要對存活對象進行標記,然后要整理存活對象內(nèi)存地址,相對于復制算法效率較低。

分代收集算法

分代收集算法,當前商用的虛擬機的垃圾收集大都采用這種算法。也不算是算法,只是把java 堆分成 新生代,老生代。分代之后,不同區(qū)域可以使用不同的收集算法。比如:

新生代 每次垃圾回收都會有大批對象死去,只有少量存活,那就采用復制-算法;

老生代 對象存活率高,也沒額外的空間對它進行分配擔保,使用標記-整理/清除算法會更好來回收。不同場景使用不同的算法更加有利于整體效率的提升。

JVM 在進行GC 時,并非每次都對上面三個內(nèi)存區(qū)域一起回收的,大部分時候回收的都是指新生代。因此GC按照回收的區(qū)域又分了兩種類型,一種是普通GC(minor GC),一種是全局GC(major GC or Full GC),它們所針對的區(qū)域如下。
普通GC(minor GC):只針對新生代區(qū)域的GC。
全局GC(major GC or Full GC):針對年老代的GC,偶爾伴隨對新生代的GC以及對永久代的GC。

注:由于年老代與永久代相對來說GC效果不好,而且二者的內(nèi)存使用增長速度也慢,因此一般情況下,需要經(jīng)過好幾次普通GC,才會觸發(fā)一次全局GC。

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://m.hztianpu.com/yun/66858.html

相關(guān)文章

  • 垃圾回收算法 JVM 垃圾回收器綜述

    摘要:垃圾回收算法與垃圾回收器綜述我們常說的垃圾回收算法可以分為兩部分對象的查找算法與真正的回收方法。串行垃圾回收器一次只使用一個線程進行垃圾回收并行垃圾回收器一次將開啟多個線程同時進行垃圾回收。 垃圾回收算法與 JVM 垃圾回收器綜述歸納于筆者的 JVM 內(nèi)部原理與性能調(diào)優(yōu)系列文章,文中涉及的引用資料參考 Java 學習與實踐資料索引、JVM 資料索引。 showImg(https://s...

    imingyu 評論0 收藏0
  • 一文了解JVM

    摘要:而使用虛擬機是實現(xiàn)這一特點的關(guān)鍵。每個字節(jié)碼指令都由一個字節(jié)的操作碼和附加的操作數(shù)組成。字節(jié)碼可以通過以下兩種方式轉(zhuǎn)換成合適的語言解釋器一條一條地讀取,解釋并執(zhí)行字節(jié)碼執(zhí)行,所以它可以很快地解釋字節(jié)碼,但是執(zhí)行起來會比較慢。 一、什么是JVM JVM是Java Virtual Machine(Java 虛擬機)的縮寫,JVM是一種用于計算設備的規(guī)范,它是一個虛構(gòu)出來的計算機,是通過在實...

    whatsns 評論0 收藏0
  • 必知必會JVM垃圾回收——對象搜索算法回收算法

    垃圾回收(GC)是JVM的一大殺器,它使程序員可以更高效地專注于程序的開發(fā)設計,而不用過多地考慮對象的創(chuàng)建銷毀等操作。但是這并不是說程序員不需要了解GC。GC只是Java編程中一項自動化工具,任何一個工具都有它適用的范圍,當超出它的范圍的時候,可能它將不是那么自動,而是需要人工去了解與適應地適用。 擁有一定工作年限的程序員,在工作期間肯定會經(jīng)常碰到像內(nèi)存溢出、內(nèi)存泄露、高并發(fā)的場景。這時候在應對這...

    LuDongWei 評論0 收藏0
  • 談一談JVM垃圾回收

    摘要:這個算法看似不錯而且簡單,不過存在這一個致命傷當兩個對象互相引用的時候,就永遠不會被回收于是引用計數(shù)算法就永遠回收不了這兩個對象,下面介紹另一種算法。 前言 ? 如果要問Java與其他編程語言最大的不同是什么,我第一個想到的一定就是Java所運行的JVM所自帶的自動垃圾回收機制,以下是我學習JVM垃圾回收機制整理的筆記,希望能對讀者有一些幫助。 哪些內(nèi)存需要回收?what? ? ...

    stormzhang 評論0 收藏0
  • Java 垃圾回收(GC) 泛讀

    摘要:在這種消耗很高的狀態(tài)下,應用程序所有的線程都會掛起,暫停一切正常的工作,等待垃圾回收的完成。但是,因為線程切換和上下文轉(zhuǎn)換的消耗,會使得垃圾回收的總體成本上升,造成系統(tǒng)吞吐量的下降。 Java 垃圾回收(GC) 泛讀 文章地址: https://segmentfault.com/a/1190000008922319 0. 序言 帶著問題去看待 垃圾回收(GC) 會比較好,一般來說主要的...

    haoguo 評論0 收藏0

發(fā)表評論

0條評論

StonePanda

|高級講師

TA的文章

閱讀更多
最新活動
閱讀需要支付1元查看
<