中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

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

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

JVM垃圾回收原理該怎么解析

發布時間:2021-10-23 17:23:47 來源:億速云 閱讀:143 作者:柒染 欄目:編程語言

這篇文章將為大家詳細講解有關JVM垃圾回收原理該怎么解析,文章內容質量較高,因此小編分享給大家做個參考,希望大家閱讀完這篇文章后對相關知識有一定的了解。

概述

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

那些垃圾需要回收

垃圾回收的基本思想是考察一個對象的可達性,即從根節點開始是否可以訪問到這個對象,如果可以,則說明對象正在被使用,相反如果從根節點無法訪問到這個對象,說明對象已經不再使用了,一般來說此對象就是需要被回收的。這個算法為根搜索算法。

可達性分析

但是實際中,一個不可達的對象有可能在某種條件下“復活”自己,那么對它的回收就是不合理的。為此給出一個對象可達性狀態的定義,并規定了在什么狀態下可以安全的回收對象。可達性對象包含了以下三種狀態。

可達的:從根節點開始,按照引用節點,可以搜索到這個對象

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

不可達的:對象的finalize()方法被調用,并且沒有復活,那么就進入不可達狀態。不可達的對象不可能會被“復活”,因為finalize()方法只能調用一次。

/** *  * <p>Description: 1.對象被GC時,可以通過finalize拯救 2.finalize只被調用一次 </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優先級地,先等待    Thread.sleep(500);    if(currentObj == null) {      System.out.println("dead");    }else {      currentObj.alive();    }    currentObj = null;    System.gc();    //finalize優先級地,先等待    Thread.sleep(500);    if(currentObj == null) {      System.out.println("dead");    }else {      currentObj.alive();    }  }}

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

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

虛擬機棧(棧幀中的本地表量表)中引用的對象  方法區中類靜態屬性引用的對象  方法區中常量引用的對象  本地方法棧中JNI引用(即一般Native的方法)的對象

四種引用類型

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

強引用

在Java中最常見的就是強引用, 把一個對象賦給一個引用變量,這個引用變量就是一個強引用。當一個對象被強引用變量引用時,它處于可達狀態,它是不可能被垃圾回收機制回收的,即使該對象以后永遠都不會被用到JVM也不會回收。因此強引用是造成Java內存泄漏的主要原因之一。

軟引用

軟引用需要用SoftReference類來實現,對于只有軟引用的對象來說,當系統內存足夠時它不會被回收,當系統內存空間不足時它會被回收。軟引用通常用在對內存敏感的程序中。

弱引用

弱引用需要用WeakReference類來實現,它比軟引用的生存期更短,對于只有弱引用的對象來說,只要垃圾回收機制一運行,不管 JVM 的內存空間是否足夠,總會回收該對象占用的內存。

虛引用

虛引用需要PhantomReference類來實現,它不能單獨使用,必須和引用隊列聯合使用。 虛引用的主要作用是跟蹤對象被垃圾回收的狀態。

什時候回收

按HotSpot VM的serial GC的實現來看觸發條件主要分為以下幾種:

young GC:當young gen中的eden區分配滿的時候觸發。注意young GC中有部分存活對象會晉升到old gen,所以young GC后old gen的占用量通常會有所升高。  full GC:當準備要觸發一次young GC時,如果發現統計數據說之前young GC的平均晉升大小比目前old gen剩余的空間大,則不會觸發young GC而是轉為觸發full GC(因為HotSpot VM的GC里,除了CMS的concurrent collection之外,其它能收集old gen的GC都會同時收集整個GC堆,包括young gen,所以不需要事先觸發一次單獨的young GC);或者,如果有perm gen的話,要在perm gen分配空間但已經沒有足夠空間時,也要觸發一次full GC;或者System.gc()、heap dump帶GC,默認也是觸發full GC。

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

如何回收

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

標記清除法(Mark-Sweep)

標記清除算法是現代垃圾回收算法的思想基礎。它主要分為兩個階段:標記階段和清除階段。在標記階段,首先通過根節點,標記所有從根節點開始的可達隊對象,因此未被標記的對象就是未被引用的垃圾對象。然后在清除階段,清除所有的未被標記的對象。

標記清除算法的不足有:效率的問題和標記清除后產生的大量不連續的內存碎片。而內存碎片太多可能會導致在分配大對象時,無法找到連續的內存而不得不提前觸發另外一次垃圾回收。

復制算法(Coping)

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

如果系統中的待回收的對象很多,復制算法需要復制的存活對象就會相對較少,真正的垃圾回收時刻,復制算法的效率就會很高。而且對象是在垃圾回收過程中的,統一復制到新的內存空間,再清除原來使用的內存,因此可以確保回收后的內存空間是沒有碎片的。但是另一方面,復制算法的代價是需要使用更多的內存空間。

復制算法比較適用于新生代。因為新生代垃圾對象通常多余存活對象,復制算法的效率會比較高。

標記整理算法(Mark Compact)

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

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

分代算法(Generational Collecting)

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

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

分區算法(Region)

一般來說,相同條件下,堆空間越大,一次GC所需要的事件越長,從而產生的停頓也越長。為了更好的靠之停頓時間,將一塊大的內存區域分割成多個大小形同的小區域,依據目標的停頓時間,每次回收若干個小區間,而不是整個堆空間,從而減少一次GC所產生的停頓。分區算法是將整個堆空間劃分為連續的不同小區間。每個小區間獨立使用,獨立回收。

關于JVM垃圾回收原理該怎么解析就分享到這里了,希望以上內容可以對大家有一定的幫助,可以學到更多知識。如果覺得文章不錯,可以把它分享出去讓更多的人看到。

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

jvm
AI

古丈县| 台州市| 万山特区| 左权县| 辉南县| 阿拉善左旗| 香港| 平泉县| 武功县| 资溪县| 海安县| 文成县| 新河县| 十堰市| 凉山| 苍梧县| 清苑县| 安溪县| 灵川县| 沁水县| 社旗县| 沐川县| 玛曲县| 海伦市| 苍南县| 响水县| 耒阳市| 长泰县| 杨浦区| 迁西县| 长阳| 布尔津县| 桂平市| 庐江县| 外汇| 水城县| 临高县| 弥勒县| 浦城县| 河间市| 于都县|