www.久久久久|狼友网站av天堂|精品国产无码a片|一级av色欲av|91在线播放视频|亚洲无码主播在线|国产精品草久在线|明星AV网站在线|污污内射久久一区|婷婷综合视频网站

當(dāng)前位置:首頁 > 公眾號精選 > 架構(gòu)師社區(qū)
[導(dǎo)讀]前言 回收,舊手機,舊冰箱,舊空調(diào),舊洗衣機,電瓶車摩托車,自行車,報紙,塑料 還記得小時候,我喝完的飲料瓶子都不會扔,每次都放到陽臺。小區(qū)里聽到收廢品的吆喝,感覺帶著這些瓶瓶罐罐沖下樓,換幾塊錢買雪糕,想想都是童年的回憶啊。 我一直都覺得騎


前言

回收,舊手機,舊冰箱,舊空調(diào),舊洗衣機,電瓶車摩托車,自行車,報紙,塑料


肝完這篇垃圾回收,和面試官扯皮不怕了


還記得小時候,我喝完的飲料瓶子都不會扔,每次都放到陽臺。小區(qū)里聽到收廢品的吆喝,感覺帶著這些瓶瓶罐罐沖下樓,換幾塊錢買雪糕,想想都是童年的回憶啊。


我一直都覺得騎個三輪車,回收廢品的大爺特別酷,因為感覺他的車上面就像哆啦A夢的口袋,翻一翻什么都有。不過這些年,隨著垃圾分類,感覺收廢品的大爺也越來越少了。


回過頭想,如果沒有這些收廢品的大爺,那我攢的瓶子也賣不了錢,家里陽臺那么多瓶子還占地方。所以你大爺就是你大爺,主動過來幫你清理垃圾,還給你錢。


所以為什么 Java 越來越流行,除了說它一處安裝,到處運行的機制以外。還因為程序員也越來越懶,跟 C/C++ 相比,Java 最適合懶人的便是引入了自動垃圾回收的機制,也就是Garage Collection(下文簡稱 GC )。


網(wǎng)上對于 Java 垃圾回收的介紹堪稱冠冕堂皇:

讓程序員專注于程序本身,不用關(guān)心內(nèi)存回收這些惱人的問題,真正讓程序員的生產(chǎn)力得到了釋放,程序員不用感知到它的存在。

說這么多,不就是程序員懶么, Java 直接幫你把臟活累活都干了。就像咱們現(xiàn)在人都愛點外賣,為什么?因為不用自己動手,吃完也不用洗碗。還有你去餐盤吃飯,吃完就走,服務(wù)員會替你收拾好這些餐盤,你不會關(guān)心服務(wù)員什么時候來收,怎么收。


大家可能會說既然 Java 這么方便,已經(jīng)幫我們完成了對垃圾的清理與回收,那 GC 方面的知識我不用了解好像也沒事吧。但是人有失手,馬有失蹄,假如突然有一天外賣小哥帶著你的外賣小哥跑路了,你必須要親自動手下廚,總不能餓死吧。


所以對于 GC,道理也是一樣的,線上的服務(wù)不遇到問題還好,出現(xiàn) Bug 或者想自己做一些性能調(diào)優(yōu)的時候,就需要對 GC 有深入了解才可以,這也是成為一名優(yōu)秀 Java 程序員的必修課!

今天就把 JVM GC 相關(guān)的知識詳細介紹一下,本文將會從以下幾個方面來講述相關(guān)知識,文字較多,相信大家耐心看了之后肯定有收獲,碼字不易,別忘了「在看」,「轉(zhuǎn)發(fā)」哦。

  • JVM 內(nèi)存區(qū)域

  • 回收策略

  • 垃圾回收經(jīng)典算法

  • 垃圾回收器對比

正文

01 JVM 內(nèi)存區(qū)域

我們首先要知道垃圾回收主要回收的是哪些數(shù)據(jù),這些數(shù)據(jù)主要在哪一塊區(qū)域,所以我們一起來看下 JVM 的內(nèi)存區(qū)域。

JDK8以前

肝完這篇垃圾回收,和面試官扯皮不怕了

在JDK8之前的虛擬機,主要包含:


(1)堆


對象實例和數(shù)組都是在堆上分配的,GC 也主要對這兩類數(shù)據(jù)進行回收,里是 GC 發(fā)生的主要區(qū)域!


(2)方法區(qū)(永久代)

方法區(qū)在 JVM 中是一個非常重要的區(qū)域,它與堆一樣是被線程共享的區(qū)域。在方法區(qū)中,存儲了每個類的信息(包括類的名稱、方法信息、字段信息)、靜態(tài)變量、常量以及編譯器編譯后的代碼等。


方法區(qū)是堆的一個邏輯部分,為了區(qū)分Java堆,它還有一個別名 Non-Heap(非堆)。相對而言,GC 對于這個區(qū)域的收集是很少出現(xiàn)的。當(dāng)方法區(qū)無法滿足內(nèi)存分配需求時,將拋出 OutOfMemoryError 異常。

隨著動態(tài)類加載的情況越來越多,這塊內(nèi)存越來越不太可控。如果設(shè)置小了,當(dāng)JVM加載的類信息容量超過了這個值,系統(tǒng)運行過程中就容易出現(xiàn)內(nèi)存溢出 OOM:PermGen 的錯誤,設(shè)置大了又浪費內(nèi)存。


(3)棧

棧是線程私有的,生命周期與線程相同,主要保存執(zhí)行方法時的局部變量表、操作數(shù)棧、動態(tài)連接和方法返回地址等信息。這塊區(qū)域是不需要進行 GC 的。


(4)程序計數(shù)器

程序計數(shù)器也是線程私有的,它里面記錄了下一次需要執(zhí)行的行號,這塊區(qū)域也不需要進行 GC。


(5)本地方法棧

本地方法棧主要為了虛擬機執(zhí)行 Java 的本地方法( Native Method)時服務(wù),這塊區(qū)域也不需要進行 GC。

JDK8之后

JDK8 最大的變化就是對 JVM 內(nèi)存空間進行了改造,主要的區(qū)別是將方法區(qū)進行了移除,并新增了元空間,元空間是放置在 JVM 內(nèi)存空間之外的直接內(nèi)存中,并且 JDK8 中對于方法區(qū)的參數(shù) PermSize 和 MaxPermSize 已經(jīng)失效。


上文咱們已經(jīng)介紹過,JDK8 之前方法區(qū)放在 JVM 之中,但是隨著動態(tài)類加載的情況越來越多,很容易因為大小的限制導(dǎo)致內(nèi)存溢出 OOM:PermGen 的錯誤。


所以JDK8 之后把使用元空間替代了原來的方法區(qū),在這種架構(gòu)下,元空間就突破了原來-XX:MaxPermSize 的限制。這樣就從一定程度上解決了原來在運行時生成大量類造成經(jīng)常 Full GC 問題,如運行時使用反射、代理等,所以升級以后Java堆空間可能會增加。

02 垃圾回收策略

凡事都講解個策略,那么 Java 怎么判斷堆中的對象實例或數(shù)據(jù)是不是垃圾呢,應(yīng)不應(yīng)該把它回收掉呢?

引用計數(shù)法

第一種最簡單粗暴的就是引用計數(shù)法。當(dāng)對象被引用,程序計數(shù)器 +1,釋放時候 -1,當(dāng)為 0 時證明對象未被引用,可以回收。

肝完這篇垃圾回收,和面試官扯皮不怕了


但是這個算法有明顯的缺陷,對于循環(huán)引用的情況下,循環(huán)引用的對象就不會被回收。例如下圖:對象 A,對象 B 循環(huán)引用,沒有其他的對象引用 A 和 B,則 A 和 B 都不會被回收。

肝完這篇垃圾回收,和面試官扯皮不怕了


可達性分析

第二種策略明顯好的多,也就是所謂可達性分析法。它指的,通過一系列稱之為“GC Roots” 的對象作為起點;從此起點向下搜索,所走過的路徑稱之為引用鏈,當(dāng)一個對象到 GC Roots 沒有任何引用鏈相連接,代表此對象不可達。


肝完這篇垃圾回收,和面試官扯皮不怕了


在 Java 可以作為GC Roots 的對象包括:


1、虛擬機棧(幀棧中的局部變量表)中的引用對象;


2、方法區(qū)中類靜態(tài)屬性引用的對象;


3、方法區(qū)中常量引用的對象;


4、本地方法棧中JNI (即一般說的 Native 方法) 的引用對象;


畫外音: GC Roots有哪些這個問題經(jīng)常在面試中被問到,大家一定牢記!

03 垃圾回收經(jīng)典算法

知道了應(yīng)該對哪些對象進行回收,那接下來就要看如何回收了,經(jīng)典的垃圾回收算法有三種。

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

肝完這篇垃圾回收,和面試官扯皮不怕了

在gc時候,首先掃描時對需要清理的無用對象進行標(biāo)記,然后將這些對象直接清理。


操作起來非常很簡單,但仔細想想有什么問題呢?


沒錯,內(nèi)存碎片!上圖,如果清理了兩個 1kb 的對象,再添加一個 2kb 的對象,是無法放入這兩個位置的。


怎么解決呢,如果能把這些碎片的內(nèi)存連起來就可以了!

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

標(biāo)記 - 整理算法就是在標(biāo)記 - 清理算法的基礎(chǔ)上,多加了一步整理的過程,把空閑的空間進行上移,從而解決了內(nèi)存碎片的問題。


但是缺點也很明顯:每進一次垃圾清除都要頻繁地移動存活的對象,效率十分低下。

復(fù)制算法

肝完這篇垃圾回收,和面試官扯皮不怕了

復(fù)制算法是將空間一分為二,在清理時,將需要保留的對象復(fù)制到第二塊區(qū)域上,復(fù)制的時候直接緊湊排列,然后把原來的一塊區(qū)域清空。


不過復(fù)制算法的缺點也顯而易見,本來 JVM 堆假設(shè)有 100M 內(nèi)存,結(jié)果由于將空間一為二,真正能用的變成只有 50M 了!這肯定是不能接受的!另外每次回收也要把存活對象移動到另一半,效率低下。

分代算法

分代收集算法整合了以上算法,綜合了這些算法的優(yōu)點,最大程度避免了它們的缺點。與其說它是算法,倒不是說它是一種策略,因為它是把上述幾種算法整合在了一起,們先從下圖看看對的生存規(guī)律。

肝完這篇垃圾回收,和面試官扯皮不怕了


由圖可知,大部分的對象都很短命,一般來說,98% 的對象都是朝生夕死的,所以分代收集算法根據(jù)對象存活周期的不同將堆分成新生代和老生代。

肝完這篇垃圾回收,和面試官扯皮不怕了

新生代和老年代的默認比例為 1 : 2,新生代又分為 Eden 區(qū), from Survivor 區(qū)(簡稱 S0 ),to Survivor 區(qū)(簡稱 S1 ),三者的比例為 8: 1 : 1。


根據(jù)新老生代的特點選擇最合適的垃圾回收算法,我們把新生代發(fā)生的 GC 稱為 Young GC(也叫 Minor GC ),老年代發(fā)生的 GC 稱為 Old GC(也稱為 Full GC )。


大多數(shù)情況下,對象在新生代 Eden區(qū)中分配。當(dāng) Eden 區(qū)沒有足夠空間進行分配時,虛擬機將發(fā)起一次 MinorGC;


Minor GC 非常頻繁,一般回收速度也比較快;出現(xiàn)了 Full GC,經(jīng)常會伴隨至少一次的 Minor GC,F(xiàn)ull GC 的速度一般會比 Minor GC 慢10倍以上。

肝完這篇垃圾回收,和面試官扯皮不怕了

整個過程大致分為以下幾個步驟:


(1)當(dāng) Eden 滿了后,進行 Minor GC,將需要保存的數(shù)據(jù)復(fù)制到 S0 中


(2)然后清空 Eden 和 S1 區(qū)域,需要保留的對象目前在 S0 中


(3)下一次當(dāng) Eden 滿了后,進行Minor GC,將原來 S0 存在的數(shù)據(jù)復(fù)制到S1中,將 Eden 中需要保存的數(shù)據(jù)也復(fù)制到 S1 中


(4)清空 Eden 和 S0 區(qū)域,需要保存的對象目前都在 S1 中


(5)Eden+S0 復(fù)制到 S1;


(6)Eden+S1 復(fù)制到 S0;


(7)Eden+S0 復(fù)制到 S1;


周而復(fù)始...

肝完這篇垃圾回收,和面試官扯皮不怕了

04 垃圾回收器對比

前面的內(nèi)容更多的是方法論,真正執(zhí)行垃圾回收的要靠各個垃圾回收器。


Java 虛擬機規(guī)范并沒有規(guī)定垃圾收集器應(yīng)該如何實現(xiàn),因此一般來說不同廠商,不同版本的虛擬機提供的垃圾收集器實現(xiàn)可能會有差別,一般會給出參數(shù)來讓用戶根據(jù)應(yīng)用的特點來組合各個年代使用的收集器,主要有以下幾種垃圾收集器。

Serial收集器

從名字看出這是一個單線程收集器。串行垃圾回收器在進行垃圾回收時,它會持有所有應(yīng)用程序的線程,凍結(jié)所有應(yīng)用程序線程,使用單個垃圾回收線程來進行垃圾回收工作。


它是 JDK1.3 之前新生代的回收器的唯一選擇,在單線程的情況效果很好,因為單線程沒有線程的切換的開銷。但是在現(xiàn)在大部分都是多 CPU 的服務(wù)器,所以它現(xiàn)在被使用的很少了。


但是它還是 JVM 運行在 Client 模式下的默認垃圾收集器。因為一般桌面應(yīng)用下新生代空間不是很大,使用這個垃圾回收器也可以保證回收的時間在 100 毫秒左右。

Serial-Old收集器

這個收集器就是 serial 收集器的老年版本,他同樣還是單線程的垃圾回收器。它存在的主要意義的還是 JVM 運行在 client 模式下的默認老年代回收器跟 serial收集器一起使用,同樣它還作為 CMS 垃圾回收器的后備垃圾回收器。


肝完這篇垃圾回收,和面試官扯皮不怕了

ParNew收集器

ParNew 垃圾收集器就是 serial 回收器的多線程版本,有很多的代碼都是和 serial 收集器公用的。一個很重要的作用就是作為新生代的垃圾回收器跟 CMS垃圾回收器進行組合。但是在單核 CPU 的情況下,效率是沒有 serial 垃圾回收器的效果好的。


可以通過-XX:UseConcMarkSweepGC 或者-XX:UseParNewGC 來指定使用它。默認情況它用于回收垃圾的線程的數(shù)目跟 CPU 的數(shù)目相同。可以通過-XX:parallelGCThreads 來指定使用的垃圾回收的線程的數(shù)目。

Parallel Scavenge集器

與 ParNew 線程一樣同樣為多線程的垃圾回收器,但是這個垃圾回收器和其他回收器的關(guān)注點不同。其他的垃圾回收器是盡可能縮短垃圾回收時對用戶線程的縮短時間。但是這個垃圾回收器關(guān)注的是一個吞吐量的概念。


吞吐量指的是運行用戶代碼的時間/(運行用戶代碼時間+垃圾回收時間)。縮短用戶停頓時間對那些高交互性比如一些 web 項目看中的。而吞吐量是一些運行在后臺的計算任務(wù)是看重的。

Parallel Old集器

這個回收器是 Parallel scavenge 的老年代版本,經(jīng)常和 Parallel scavenge 一起使用在對內(nèi)存比較敏感和對吞吐量比較高的場合下使用,使用多線程和“標(biāo)記-整理”算法。這個收集器是在JDK 1.6 中才開始提供

CMS集器(劃重點?。。?/span>

CMS( Concurrent Mark Sweep )收集器是一種以獲取最短回收停頓時間為目標(biāo)的收集器。目前很大一部分的Java應(yīng)用都集中在互聯(lián)網(wǎng)站或 B/S 系統(tǒng)的服務(wù)端上,這類應(yīng)用尤其重視服務(wù)的響應(yīng)速度,希望系統(tǒng)停頓時間最短,以給用戶帶來較好的體驗。CMS 基于“標(biāo)記-清除”算法實現(xiàn)的,整個過程分為 4 個步驟,包括:

(1)初始標(biāo)記:只標(biāo)記根節(jié)點直接關(guān)聯(lián)的引用對象,需要暫停用戶線程(時間短)


(2)并發(fā)標(biāo)記:標(biāo)記其他引用對象,可以跟用戶線程并發(fā)同時執(zhí)行


(3)重新標(biāo)記:暫停用戶線程,對并發(fā)標(biāo)記期間新增加的引用關(guān)系變化再次標(biāo)記(時間短)


(4)并發(fā)清除:跟用戶線程并發(fā)進行


其中初始標(biāo)記、重新標(biāo)記這兩個步驟仍然需要“Stop The World”。初始標(biāo)記僅僅只是標(biāo)記一下 GC Roots 能直接關(guān)聯(lián)到的對象,速度很快,并發(fā)標(biāo)記階段就是進行 GC Roots Tracing 的過程,而重新標(biāo)記階段則是為了修正并發(fā)標(biāo)記期間,因用戶程序繼續(xù)運作而導(dǎo)致標(biāo)記產(chǎn)生變動的那一部分對象的標(biāo)記記錄,這個階段的停頓時間一般會比初始標(biāo)記階段稍長一些,但遠比并發(fā)標(biāo)記的時間短。


由于整個過程中耗時最長的并發(fā)標(biāo)記和并發(fā)清除過程中,收集器線程都可以與用戶線程一起工作,所以總體上來說,CMS 收集器的內(nèi)存回收過程是與用戶線程一起并發(fā)地執(zhí)行。


CMS 收集器已經(jīng)在很大程度上減少了用戶線程的停頓時間,但是他也存在下面三個主要的缺點:

(1)跟用戶線程競爭資源

CMS 默認的并發(fā)線程數(shù)目為(CPU 數(shù)目+3)/4,當(dāng) CPU 線程大于 4 的時候,CMS 垃圾收集器至少要占用 25% 的資源。當(dāng)小于 4 的時候占用 CPU 資源更加明顯。


(2)無法清除浮動垃圾

當(dāng)收集器在進行并發(fā)清除垃圾的時候,由于用戶線程還在執(zhí)行,要預(yù)留一定的空間給用戶線程進行使用,所以收集器一定不能在老年代已經(jīng)占用 100% 的情況下再進行垃圾收集。

(3)內(nèi)存碎片

因為這個垃圾回收器是使用的標(biāo)記-清除算法,所以會產(chǎn)生大量的內(nèi)存碎片。


有兩個值可以進行控制:

-XX:UseCMSCompactAtFullCollection 默認開啟,來指定需要 FULL GC 時,會對內(nèi)存空間進行一次整理。

-XX:CMSFullGCsBeforeCompaction 來指定多少次不整理之后進行一次整理。

肝完這篇垃圾回收,和面試官扯皮不怕了

G1收集器(劃重點!?。?/strong>

G1 是目前技術(shù)發(fā)展的最前沿成果之一,HotSpot 開發(fā)團隊賦予它的使命是未來可以替換掉JDK1.5中發(fā)布的CMS收集器。CMS 收集器相比 G1 收集器有以下特點:


(1)空間整合:G1 收集器采用標(biāo)記-整理算法,不會產(chǎn)生內(nèi)存空間碎片。分配大對象時不會因為無法找到連續(xù)空間而提前觸發(fā)下一次 GC。


(2)可預(yù)測停頓:這是 G1 的另一大優(yōu)勢,降低停頓時間是 G1 和 CMS 的共同關(guān)注點,但 G1 除了追求低停頓外,還能建立可預(yù)測的停頓時間模型。能讓使用者明確指定在一個長度為 N 毫秒的時間片段內(nèi),消耗在垃圾收集上的時間不得超過 N 毫秒,這幾乎已經(jīng)是實時垃圾收集器的特征了。


上面提到的垃圾收集器,收集的范圍都是整個新生代或者老年代,而 G1 不再是這樣。使用 G1 收集器時,Java 堆的內(nèi)存布局與其他收集器有很大差別,它將整個Java堆劃分為多個大小相等的獨立區(qū)域(Region),雖然還保留有新生代和老年代的概念,但新生代和老年代不再是物理隔閡了,它們都是一部分可以不連續(xù) Region 的集合。

肝完這篇垃圾回收,和面試官扯皮不怕了

05 總結(jié)

本文介紹了 JVM 垃圾回收的原理與垃圾收集器的種類,相信看到這里的各位人才應(yīng)該對相關(guān)知識有了更深刻的認識。


理論有了,接下來我會持續(xù)更新相關(guān)內(nèi)容,介紹下真實場景下如何對 JVM進行調(diào)優(yōu)以及故障排查。


特別推薦一個分享架構(gòu)+算法的優(yōu)質(zhì)內(nèi)容,還沒關(guān)注的小伙伴,可以長按關(guān)注一下:

肝完這篇垃圾回收,和面試官扯皮不怕了

肝完這篇垃圾回收,和面試官扯皮不怕了

肝完這篇垃圾回收,和面試官扯皮不怕了

長按訂閱更多精彩▼

肝完這篇垃圾回收,和面試官扯皮不怕了

如有收獲,點個在看,誠摯感謝

免責(zé)聲明:本文內(nèi)容由21ic獲得授權(quán)后發(fā)布,版權(quán)歸原作者所有,本平臺僅提供信息存儲服務(wù)。文章僅代表作者個人觀點,不代表本平臺立場,如有問題,請聯(lián)系我們,謝謝!

本站聲明: 本文章由作者或相關(guān)機構(gòu)授權(quán)發(fā)布,目的在于傳遞更多信息,并不代表本站贊同其觀點,本站亦不保證或承諾內(nèi)容真實性等。需要轉(zhuǎn)載請聯(lián)系該專欄作者,如若文章內(nèi)容侵犯您的權(quán)益,請及時聯(lián)系本站刪除。
關(guān)閉
關(guān)閉