自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

JVM內(nèi)存分代、垃圾回收漫談

開(kāi)發(fā) 前端
最近在看周志明老師的《深入理解 Java 虛擬機(jī)》一書(shū),收獲頗多,以下為看完前半部分后的一些算是讀書(shū)筆記吧,結(jié)合書(shū)本內(nèi)容,簡(jiǎn)單記錄分享一下有關(guān) JVM 內(nèi)存分代以及垃圾回收相關(guān)的內(nèi)容。

最近在看周志明老師的《深入理解 Java 虛擬機(jī)》一書(shū),收獲頗多,以下為看完前半部分后的一些算是讀書(shū)筆記吧,結(jié)合書(shū)本內(nèi)容,簡(jiǎn)單記錄分享一下有關(guān) JVM 內(nèi)存分代以及垃圾回收相關(guān)的內(nèi)容。

[[189490]]

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

都知道 JVM 的內(nèi)存區(qū)域分為5個(gè)部分,如果有疑惑,可以參看之前的一篇文章 -JVM 內(nèi)存區(qū)域介紹。

這里也簡(jiǎn)單羅列一下 JVM 的五部分

程序計(jì)數(shù)器

這是一塊較小的內(nèi)存空間,它的作用可以看做是當(dāng)前線程所執(zhí)行的字節(jié)碼的行號(hào)指示器,線程私有。

Java 虛擬機(jī)棧

它是 Java方法執(zhí)行的內(nèi)存模型,每一個(gè)方法被調(diào)用到執(zhí)行完成的過(guò)程,就對(duì)應(yīng)著一個(gè)棧幀在虛擬機(jī)棧中從入棧到出棧的過(guò)程,線程私有。

本地方法棧

跟虛擬機(jī)棧類似,不過(guò)本地方法棧用于執(zhí)行本地方法,線程私有。

Java 堆

該區(qū)域存在的唯一目的就是存放對(duì)象,幾乎應(yīng)用中所有的對(duì)象實(shí)例都在這里分配內(nèi)存,所有線程共享。

方法區(qū)

它用于存儲(chǔ)已被虛擬機(jī)加載的類信息、常量、靜態(tài)變量、即時(shí)編譯器編譯后的代碼等數(shù)據(jù),所有線程共享。

有關(guān) OOM

都知道,任何一個(gè)應(yīng)用在啟動(dòng)后,操作系統(tǒng)分配給它的內(nèi)存一定是有限的,所以如何合理有效的管理內(nèi)存,就變得尤為重要。

而從上節(jié)可知,我們一般討論的對(duì)象內(nèi)存分配均發(fā)生在 Java 堆上。所以這里說(shuō)的內(nèi)存管理大部分情況下即指對(duì) Java 堆內(nèi)存。而程序計(jì)數(shù)器、虛擬機(jī)棧他們隨著線程生而生,亡而亡,所以他們內(nèi)存相對(duì)比較好管理,出現(xiàn)的問(wèn)題也比較少。

一個(gè)應(yīng)用啟動(dòng)后,不停運(yùn)行,不停的執(zhí)行命令,創(chuàng)建對(duì)象,而這些對(duì)象,大都存放在堆內(nèi)存區(qū)域。這部分區(qū)域的大小是有限的,而需要生成的對(duì)象是無(wú)限的,當(dāng)某一次創(chuàng)建對(duì)象時(shí)發(fā)現(xiàn)堆內(nèi)存實(shí)在沒(méi)有空間可用來(lái)創(chuàng)建對(duì)象的時(shí)候,JVM 就會(huì)爆出 OutOfMemoryError 異常(后文統(tǒng)稱 OOM),程序就會(huì)掛掉。

上面只是說(shuō)明了一下表象。其實(shí) OOM 遠(yuǎn)不是上面說(shuō)的那么簡(jiǎn)單。如果要理解 OOM,這里還有一些其他知識(shí)需要說(shuō)明。

  • OOM 發(fā)生前其實(shí) JVM 會(huì)進(jìn)行內(nèi)存的垃圾回收(GC)。
  • 垃圾回收有多種不同的實(shí)現(xiàn)算法。
  • 為了更好的管理內(nèi)存,堆內(nèi)存進(jìn)行了分代。
  • 堆內(nèi)存的新生代和老年代的垃圾回收算法不一致。

其實(shí),這里的知識(shí)需要綜合理解,你才會(huì)對(duì) OOM 有一個(gè)全面的認(rèn)識(shí)。

內(nèi)存分代

一個(gè)應(yīng)用啟動(dòng),操作系統(tǒng)會(huì)給他分配一個(gè)初始的內(nèi)存大小,由上可知,這部分內(nèi)存大部分應(yīng)該屬于堆內(nèi)存,JVM 為了更好地利用管理這部分內(nèi)存,對(duì)該區(qū)域做了劃分。一部分成為新生代,另一部分稱為老年代。

一開(kāi)始對(duì)象的創(chuàng)建都發(fā)生在新生代,隨著對(duì)象的不斷創(chuàng)建,如果新生代沒(méi)有空間創(chuàng)建新對(duì)象,將會(huì)發(fā)生 GC ,這時(shí)的 GC 稱之為 Minor GC,位于新生代的對(duì)象每經(jīng)過(guò)一次 Minor GC 后,如果這個(gè)對(duì)象沒(méi)有被回收,則為自己的標(biāo)記數(shù)加1,這個(gè)標(biāo)記數(shù)用于標(biāo)識(shí)這個(gè)對(duì)象經(jīng)歷了多少次的 Minor GC,對(duì)于 Sun 的 Hotspot 虛擬機(jī),如果這個(gè)次數(shù)超過(guò) 15 ,該對(duì)象才會(huì)被移動(dòng)到老年代。

隨著時(shí)間的推移,如果老年代也沒(méi)有足夠的空間容納對(duì)象,老年代也會(huì)試著發(fā)起 GC,這時(shí)的 GC 被稱為 Full GC。

相比 Minor GC,F(xiàn)ull GC 發(fā)生的次數(shù)比較少,但是每發(fā)生一次 Full GC,整個(gè)堆內(nèi)存區(qū)域都需要執(zhí)行一次垃圾回收,這對(duì)程序性能造成的影響比 Minor GC 大很多。所以我們應(yīng)該盡量避免或者減少 Full GC 的發(fā)生。

同時(shí),在堆內(nèi)存區(qū)域,發(fā)生最多的 GC 情形就是新生代的 Minor GC 了,因?yàn)樗械膶?duì)象會(huì)優(yōu)先去新生代開(kāi)辟空間,所以這塊的內(nèi)存變化會(huì)很快,只有內(nèi)存不夠用,就會(huì)發(fā)生 GC,但是一般的 Minor GC 執(zhí)行比 Full GC 快很多。為什么呢?因?yàn)樾律屠夏甏睦厥账惴ú灰粯印?/p>

垃圾回收算法

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

這是最基礎(chǔ)的收集算法,如它的名字一樣,算法分為“標(biāo)記”和“清除”兩個(gè)階段:

首先標(biāo)記出所有需要回收的對(duì)象,在標(biāo)記完成后統(tǒng)一回收掉所有被標(biāo)記的對(duì)象。

之所以說(shuō)它是最基礎(chǔ)的收集算法,是因?yàn)楹罄m(xù)的收集算法都是基于這種思路并對(duì)其缺點(diǎn)進(jìn)行改進(jìn)而得到的。

它的主要缺點(diǎn)有兩個(gè):一個(gè)是效率問(wèn)題,標(biāo)記和清除過(guò)程的效率都不高;另外一個(gè)是空間問(wèn)題,標(biāo)記清除之后會(huì)產(chǎn)生大量不連續(xù)的內(nèi)存碎片,空間碎片太多可能會(huì)導(dǎo)致,當(dāng)程序在以后的運(yùn)行過(guò)程中需要分配較大對(duì)象時(shí)無(wú)法找到足夠的連續(xù)內(nèi)存而不得不提前觸發(fā)另一次垃圾收集動(dòng)作。

復(fù)制算法(Copying)

為了解決效率問(wèn)題,一種稱為“復(fù)制”(Copying)的收集算法出現(xiàn)了,它將可用內(nèi)存按容量劃分為大小相等的兩塊,每次只使用其中的一塊。當(dāng)這一塊的內(nèi)存用完了,就將還存活著的對(duì)象復(fù)制到另外一塊上面,然后再把已使用過(guò)的內(nèi)存空間一次清理掉。

這樣使得每次都是對(duì)其中的一塊進(jìn)行內(nèi)存回收,內(nèi)存分配時(shí)也就不用考慮內(nèi)存碎片等復(fù)雜情況,只要移動(dòng)堆頂指針,按順序分配內(nèi)存即可,實(shí)現(xiàn)簡(jiǎn)單,運(yùn)行高效。只是這種算法的代價(jià)是將內(nèi)存縮小為原來(lái)的一半,未免太高了一點(diǎn)。

但是這種算法的效率相當(dāng)高,所以,現(xiàn)在的商業(yè)虛擬機(jī)都采用這種收集算法來(lái)回收新生代。為什么新生代可以使用復(fù)制算法呢?

IBM 有專門研究表明,新生代中的對(duì)象 98% 都是朝生夕死,所以就不需要按照1:1的比例來(lái)劃分內(nèi)存空間。這里鑒于此,新生代采用了如下的劃分策略。

現(xiàn)在把新生代再劃分為三部分,一塊較大的 Eden(伊甸園) 和兩塊較小的 Survivor(幸存者) 區(qū)域。

當(dāng)回收時(shí),將 Eden 和 Survivor 中還存活著的對(duì)象一次性地拷貝到另外一塊Survivor空間上,最后清理掉Eden和剛才用過(guò)的Survivor的空間。HotSpot 虛擬機(jī)默認(rèn)Eden和Survivor的大小比例是8∶1,也就是每次新生代中可用內(nèi)存空間為整個(gè)新生代容量的90%(80%+10%),只有10%的內(nèi)存是會(huì)被“浪費(fèi)”的。

這樣清理完成后,原來(lái)的 Survivor 就空了,并一直保持為空,直到下次 Minor GC 時(shí),它再作為存活對(duì)象的盛放地。兩個(gè) Survivor 就這樣輪流當(dāng)做 GC 過(guò)程中新生代存活對(duì)象的中轉(zhuǎn)站。

但是,如果使用復(fù)制算法的內(nèi)存區(qū)域有大量的存活對(duì)象時(shí),復(fù)制算法就會(huì)變得捉襟見(jiàn)肘,這時(shí)需要更大的 Survivor 區(qū)用于盛放那些存活對(duì)象,甚至可能需要 1:1的比例。所以針對(duì)堆內(nèi)存區(qū)域的老年代,就有了下面的算法。

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

標(biāo)記過(guò)程仍然與“標(biāo)記-清除”算法一樣,但后續(xù)步驟不是直接對(duì)可回收對(duì)象進(jìn)行清理,而是 讓所有存活的對(duì)象都向一端移動(dòng),然后直接清理掉端邊界以外的內(nèi)存 。這種方法避免了碎片的產(chǎn)生,同時(shí)也不需要一塊額外的內(nèi)存空間,對(duì)于老年代會(huì)比較合適。

但是相比復(fù)制算法,雖然該算法占用的內(nèi)存空間少,但是耗費(fèi)的垃圾回收時(shí)間會(huì)比復(fù)制算法久,所以上面也說(shuō)了

  • 我們應(yīng)該盡量避免或者減少 Full GC 的發(fā)生。
  • 這兩種算法用精煉的語(yǔ)言描述就是
  • 復(fù)制算法:用空間換時(shí)間
  • 標(biāo)記-整理算法:用時(shí)間換空間

一句話 魚(yú)與熊掌不可兼得,但是針對(duì)新生代和老年代,他們都是最佳的選擇。

總結(jié)

簡(jiǎn)單梳理一下文中講到的一些知識(shí)點(diǎn)

  • 為了更好的管理堆內(nèi)存,該區(qū)域分為新生代和老年代。
  • 新生代發(fā)生垃圾回收要比老年代頻繁。
  • 新生代發(fā)生的垃圾回收成為 Minor GC;老年代發(fā)生的 GC 成為 Full GC。
  • 為了更高效管理新生代的內(nèi)存,按照復(fù)制算法,結(jié)合 IBM 的研究論證,新生代分為三塊,一塊比較大的 Eden 區(qū)和兩塊比較小的 Survivor 區(qū),比例為 8:1:1

參考

《深入理解 Java 虛擬機(jī)》- 周志明老師

責(zé)任編輯:未麗燕 來(lái)源: 咕咚
相關(guān)推薦

2021-11-05 15:23:20

JVM回收算法

2010-09-27 09:01:26

JVM分代垃圾回收

2009-12-25 16:15:31

JVM垃圾回收算法

2010-01-06 09:28:08

JVM分代垃圾回收

2011-12-05 12:51:58

JVMJava

2012-01-10 11:19:35

JavaJVM

2010-01-14 11:28:54

JVM分代垃圾回收

2010-09-26 16:42:04

JVM內(nèi)存組成JVM垃圾回收

2010-09-26 13:29:46

JVM垃圾回收

2017-08-04 10:53:30

回收算法JVM垃圾回收器

2022-01-20 10:34:49

JVM垃圾回收算法

2023-08-08 10:29:55

JVM優(yōu)化垃圾回收

2022-03-21 11:33:11

JVM垃圾回收器垃圾回收算法

2009-12-30 10:14:29

JVM垃圾回收

2010-09-25 15:33:19

JVM垃圾回收

2022-06-22 09:54:45

JVM垃圾回收Java

2023-08-27 21:29:43

JVMFullGC調(diào)優(yōu)

2012-01-10 14:25:36

JavaJVM

2023-12-19 21:52:51

Go垃圾回收開(kāi)發(fā)

2021-08-13 08:15:23

JVM 虛擬機(jī)Java
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)