<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          JVM解密 -- GC的算法和種類一覽

          共 3005字,需瀏覽 7分鐘

           ·

          2021-06-01 01:59

          點(diǎn)擊上方 Java學(xué)習(xí)之道,選擇 設(shè)為星標(biāo)

          每天18:30點(diǎn),干貨準(zhǔn)時奉上!

          來源: cnblogs.com/qdhxhz/p/9211095.html
          作者: 雨點(diǎn)的名字

          對于垃圾收集(GC), 我們需要考慮三件事情:哪些內(nèi)存需要回收?如何判斷是垃圾對象?垃圾回收算法有哪些?

          Part1一、GC的工作區(qū)域

          1不是GC的工作區(qū)域

          • (1)程序計數(shù)器、虛擬機(jī)棧和本地方法棧三個區(qū)域是線程私有的,隨線程生而生,隨線程滅而滅;

          • (2)棧中的棧幀隨著方法的進(jìn)入和退出而進(jìn)行入棧和出棧操作,每個棧幀中分配多少內(nèi)存基本上是在類結(jié)構(gòu)確定下來時就已知的,因此這幾個區(qū)域的內(nèi)存分配和回收都具有確定性。

          在這幾個區(qū)域不需要過多考慮回收的問題,因?yàn)榉椒ńY(jié)束或線程結(jié)束時,內(nèi)存自然就跟隨著回收了。

          2GC的工作區(qū)域(哪些內(nèi)存需要GC回收?)

          (1)垃圾回收重點(diǎn)關(guān)注的是堆和方法區(qū)部分的內(nèi)存。

          因?yàn)橐粋€接口中的多個實(shí)現(xiàn)類需要的內(nèi)存可能不一樣,一個方法的多個分支需要的內(nèi)存也可能不一樣,我們只有在程序處于運(yùn)行期間才能知道會創(chuàng)建哪些對象,這部分內(nèi)存的分 配和回收都是動態(tài)的,所以垃圾回收器所關(guān)注的主要是這部分的內(nèi)存。

          Part2二、垃圾對象的判定

          Java堆中存放著幾乎所有的對象實(shí)例,垃圾收集器對堆中的對象進(jìn)行回收前,要先確定這些對象是否還有用,哪些還活著。對象死去的時候才需要回收。

          3引用計數(shù)法

          引用計數(shù)法的邏輯是:在堆中存儲對象時,在對象頭處維護(hù)一個counter計數(shù)器,如果一個對象增加了一個引用與之相連,則將counter++。

          如果一個引用關(guān)系失效則counter–。如果一個對象的counter變?yōu)?,則說明該對象已經(jīng)被廢棄,不處于存活狀態(tài)。

          優(yōu)點(diǎn)

          • 1)可即刻回收垃圾,每個對象都知道自己的被引用數(shù),當(dāng)counter為0時,對象就會把自己作為空閑空間連接到空閑鏈表,也就是在對象變成垃圾的同時就會被回收.

          • 2)最大暫停時間短,每次通過指向mutator生成垃圾時,這部分垃圾都會被回收,大幅削減了mutator的最大暫停時間。

          缺點(diǎn)

          • 1)引用和去引用伴隨加法和減法,影響性能

          • 2)很難處理循環(huán)引用

          4可達(dá)性分析算法

          這種算法的基本思路是通過一系列名為“GC Roots”的對象作為起始點(diǎn),從這些節(jié)點(diǎn)開始向下搜索,搜索所走過的路徑稱為引用鏈,當(dāng)一個對象到GC Roots沒有任何引用鏈相連時,就證明此對象是不可用的。

          Java語言是通過可達(dá)性分析算法來判斷對象是否存活的。

          在Java語言里,可作為GC Roots的對象包括下面幾種:

          (1)虛擬機(jī)棧(棧幀中的本地變量表)中引用的對象。

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

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

          (4)本地方法棧中JNI(Native方法)的引用對象。

          Part3三、垃圾回收算法

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

          簡單來說有兩個步驟:標(biāo)記、清除。

          • (1). 標(biāo)記階段:找到所有可訪問的對象,做個標(biāo)記

          • (2). 清除階段:遍歷堆,把未被標(biāo)記的對象回收

          缺  點(diǎn)

          • (1)因?yàn)樯婕按罅康膬?nèi)存遍歷工作,所以執(zhí)行性能較低,這也會導(dǎo)致“stop the world”時間較長,java程序吞吐量降低;

          • (2)對象被清除之后,被清除的對象留下內(nèi)存的空缺位置會造成內(nèi)存不連續(xù),空間浪費(fèi)。

          6標(biāo)記整理(壓縮)算法

          標(biāo)記-整理算法適合用于存活對象較多的場合,如老年代。它在標(biāo)記-清除算法的基礎(chǔ)上做了一些優(yōu)化。

          • (1)、標(biāo)記階段:它的第一個階段與標(biāo)記/清除算法是一模一樣的。

          • (2)、整理階段:移動所有存活的對象,且按照內(nèi)存地址次序依次排列,然后將末端內(nèi)存地址以后的內(nèi)存全部回收。

          上圖中可以看到,標(biāo)記的存活對象將會被整理,按照內(nèi)存地址依次排列,而未被標(biāo)記的內(nèi)存會被清理掉。如此一來,當(dāng)我們需要給新對象分配內(nèi)存時,JVM只需要持有一個內(nèi)存的起始地址即可,這比維護(hù)一個空閑

          列表顯然少了許多開銷。

          優(yōu)點(diǎn)

          標(biāo)記/整理算法不僅可以彌補(bǔ)標(biāo)記/清除算法當(dāng)中,內(nèi)存區(qū)域分散的缺點(diǎn),也消除了復(fù)制算法當(dāng)中,內(nèi)存減半的高額代價。

          缺點(diǎn)

          標(biāo)記/整理算法唯一的缺點(diǎn)就是效率也不高。不僅要標(biāo)記所有存活對象,還要整理所有存活對象的引用地址。從效率上來說,標(biāo)記/整理算法要低于復(fù)制算法。

          7復(fù)制算法

          復(fù)制算法簡單來說就是把內(nèi)存一分為二,但只使用其中一份,在垃圾回收時,將正在使用的那份內(nèi)存中存活的對象復(fù)制到另一份空白的內(nèi)存中,最后將正在使用的內(nèi)存空間的對象清除,完成垃圾回收。

          優(yōu)點(diǎn)

          復(fù)制算法使得每次都只對整個半?yún)^(qū)進(jìn)行內(nèi)存回收,內(nèi)存分配時也就不用考慮內(nèi)存碎片等復(fù)雜情況,只要移動堆頂指針,按順序分配內(nèi)存即可,實(shí)現(xiàn)簡單,運(yùn)行高效。缺點(diǎn) 復(fù)制算法的代價是將內(nèi)存縮小為原來的一半,這個太要命了。

          注意(重要)

          現(xiàn)在的虛擬機(jī)使用復(fù)制算法來進(jìn)行新生代的內(nèi)存回收。因?yàn)樵谛律薪^大多數(shù)的對象都是“朝生夕亡”,所以不需要將整個內(nèi)存分為兩個部分,而是分為三個部分,一塊為Eden(伊面區(qū))和兩塊較小的

          Survivor(幸存區(qū))空間(默認(rèn)比例->8:1:1)。每次使用Eden和其中的一塊Survivor,垃圾回收時候?qū)⑸鲜鰞蓧K中存活的對象復(fù)制到另外一塊Survivor上,同時清理上述Eden和Survivor。所以每次新生代就可以使用90%

          的內(nèi)存。只有10%的內(nèi)存是浪費(fèi)的。(不能保證每次新生代都少于10%的對象存活,當(dāng)在垃圾回收復(fù)制時候如果一塊Survivor不夠時候,需要老年代來分擔(dān),大對象直接進(jìn)入老年代)

          總的來講:復(fù)制算法不適用于存活對象較多的場合,如老年代(復(fù)制算法適合做新生代的GC)

          8三種算法總結(jié)

          相同點(diǎn)

          • (1)三個算法都基于根搜索算法去判斷一個對象是否應(yīng)該被回收,而支撐根搜索算法可以正常工作的理論依據(jù),就是語法中變量作用域的相關(guān)內(nèi)容。

          • (2)在GC線程開啟時,或者說GC過程開始時,它們都要暫停應(yīng)用程序(stop the world)。

          區(qū)別

          三種算法比較:

          效率:復(fù)制算法>標(biāo)記-整理算法>標(biāo)記-清除算法;

          內(nèi)存整齊度:復(fù)制算法=標(biāo)記-整理算法>標(biāo)記-清除算法

          內(nèi)存利用率:標(biāo)記-整理算法=標(biāo)記-清除算法>復(fù)制算法

          9分代收集算法

          首先這不是一種新算法,它是一種思想?,F(xiàn)在使用的Java虛擬機(jī)并不是只是使用一種內(nèi)存回收機(jī)制,而是分代收集的算法。就是將內(nèi)存根據(jù)對象存活的周期劃分為幾塊。一般是把堆分為新生代、和老年代。短命對象存放在新生代中,長命對象放在老年代中。

          這個圖是我拷貝來的,但要記住java8以后,已經(jīng)沒有永久區(qū)了,之前永久區(qū)存放的東西基本上放到了元空間中。

          對于不同的代,采用不同的收集算法:

          新生代:由于存活的對象相對比較少,因此可以采用復(fù)制算法該算法效率比較快。

          老年代:由于存活的對象比較多哈,可以采用標(biāo)記-清除算法或是標(biāo)記-整理算法。

          -- END --

           | 更多精彩文章 -



          加我微信,交個朋友
          長按/掃碼添加↑↑↑

          瀏覽 45
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  亚洲黄色免费电影 | 免费看一级黄 | 国产超碰青青草 | 黄色操逼 | 国产日产精品一区二区三区 |