摘要:對(duì)象存不進(jìn)去,會(huì)又一次觸發(fā)垃圾回收。也就是說(shuō),它在進(jìn)行垃圾回收時(shí),必須暫停其他所有線程。我們來(lái)看一個(gè)名詞吞吐量。吞吐量運(yùn)行用戶代碼時(shí)間運(yùn)行用戶代碼時(shí)間垃圾收集時(shí)間。也就是說(shuō),收集器會(huì)嚴(yán)格控制吞吐量,至于這個(gè)吞吐量是多少,這個(gè)可以人為設(shè)置。
與其他語(yǔ)言相比,例如c/c++,我們都知道,java虛擬機(jī)對(duì)于程序中產(chǎn)生的垃圾,虛擬機(jī)是會(huì)自動(dòng)幫我們進(jìn)行清除管理的,而像c/c++這些語(yǔ)言平臺(tái)則需要程序員自己手動(dòng)對(duì)內(nèi)存進(jìn)行釋放。
雖然這種自動(dòng)幫我們回收垃圾的策略少了一定的靈活性,但卻讓代碼編寫者省去了很多工作,同時(shí)也提高了很多安全性。(因?yàn)橄馛/C++假如你創(chuàng)建了大量的對(duì)象,但卻由于自己的疏忽忘了將他們進(jìn)行釋放,可能會(huì)造成內(nèi)存溢出)。
何為垃圾?剛才說(shuō)了,虛擬機(jī)會(huì)自動(dòng)幫助我們進(jìn)行垃圾的清除,那什么樣的對(duì)象我們才可以稱為是垃圾對(duì)象呢?
假如你創(chuàng)建了一個(gè)對(duì)象
Man m = new Man();
你用一個(gè)變量指向了這個(gè)對(duì)象,顯然對(duì)于這個(gè)對(duì)象,你可以用變量m對(duì)這個(gè)對(duì)象進(jìn)行利用,但過(guò)了一段時(shí)間,你執(zhí)行了
m = null;
并且也并沒有新的變量來(lái)指向剛才創(chuàng)建的對(duì)象。此時(shí)對(duì)于這個(gè)沒有任何變量指向的對(duì)象,你覺得它還有用處嗎?
顯然,對(duì)于這種沒有被變量指向的對(duì)象,它是一點(diǎn)卵用也沒有的,它只能在堆隨風(fēng)漂流。
因此,對(duì)于這樣的對(duì)象,我們就可以把它稱為垃圾了,它早晚會(huì)被垃圾回收器給干掉。
怎么知道它已經(jīng)是垃圾對(duì)象了?假如代碼是你自己編寫的,你可能知道這個(gè)對(duì)象啥時(shí)候應(yīng)該被拋棄,你可以隨時(shí)讓它成為垃圾對(duì)象。
但是,你畢竟是你,虛擬機(jī)則沒那么智能。那虛擬機(jī)是如何知道的呢?
上面已經(jīng)說(shuō)了,沒有變量引用這個(gè)對(duì)象時(shí),它就是垃圾對(duì)象了,基于這個(gè)原理,我們可以這樣做啊:
我們可以為這個(gè)對(duì)象設(shè)置一個(gè)計(jì)數(shù)器,初始值為0,假如有一個(gè)變量指向它,那么計(jì)數(shù)器就加1,如果這個(gè)變量不在指向它了,計(jì)數(shù)器就減1。那么我們就可以判斷,如果這個(gè)計(jì)數(shù)器為0的話,那它就是垃圾對(duì)象了,否則就是有用的對(duì)象。
對(duì)于這種方法,我們稱之為引用計(jì)數(shù)法。
好吧,我們先來(lái)夸一夸引用計(jì)數(shù)法這種方法:
實(shí)現(xiàn)簡(jiǎn)單。
效率高(一個(gè)if語(yǔ)句就能解決的問(wèn)題想不高效都難)。
不好意思,接下來(lái)得說(shuō)說(shuō)它那個(gè)致命的缺點(diǎn)。
實(shí)際上,對(duì)于這種引用計(jì)數(shù)的方法,假如它遇到對(duì)象互相引用的話,是很難解決的。
先看一段代碼:
Man m1 = new Man(); Man m2 = new Man(); //互相引用 m1.instance = m2;//假設(shè)Man有instance這個(gè)屬性 m2.instance = m1; m1 = null; m2 = null; System.gc();//按道理對(duì)象應(yīng)該被回收
這段代碼m1和m2都指向null了,按道理兩個(gè)對(duì)象已經(jīng)是無(wú)用對(duì)象,應(yīng)該被回收,但是,兩個(gè)對(duì)象之間彼此有一個(gè)instance的屬性互相牽引的對(duì)方,導(dǎo)致兩個(gè)對(duì)象并沒有被回收。
這個(gè)缺點(diǎn)夠致命吧?
所以,虛擬機(jī)并沒有采用這種引用計(jì)數(shù)的方法。
可達(dá)性分析除了這種方法,我們還有其他的方法嗎?
答案是有的,必須得有啊。這種方法就是傳說(shuō)中的可達(dá)性分析,(我靠,聽名字是真的高級(jí)啊)。它的工作原理是這樣的:
在程序開始時(shí),會(huì)建立一個(gè)引用根節(jié)點(diǎn)(GC Roots),并構(gòu)建一個(gè)引用圖。當(dāng)需要判斷誰(shuí)是垃圾時(shí),我們可以從這個(gè)根節(jié)點(diǎn)進(jìn)行遍歷,如果沒有被遍歷到的節(jié)點(diǎn)則是垃圾對(duì)象,否則就是有用對(duì)象。如下圖:
這個(gè)方法可以解決循環(huán)相互引用的問(wèn)題,但是這個(gè)方法并沒有引用計(jì)數(shù)法高效,畢竟要遍歷圖啊。
總結(jié)下判斷是否為垃圾對(duì)象的算法:
引用計(jì)數(shù)法。
可達(dá)性分析。
何時(shí)進(jìn)行垃圾回收可能有人會(huì)覺得這個(gè)問(wèn)題很奇怪,覺得看到垃圾就回收不是很好。對(duì)于這個(gè)我只能說(shuō):
看到房間有一點(diǎn)垃圾你會(huì)馬上掃?還是等到某個(gè)時(shí)間點(diǎn)或者當(dāng)垃圾積累到一定的數(shù)量再掃?
虛擬機(jī)可沒那么智能可以馬上識(shí)別這個(gè)對(duì)象是垃圾對(duì)象,它還得遍歷所有對(duì)象才能知道有哪些是垃圾對(duì)象。
所以說(shuō),你總不能幾秒(我們假設(shè)幾秒是賊短的時(shí)間)就讓虛擬機(jī)遍歷一下所有對(duì)象吧?
這里先說(shuō)明一下,當(dāng)垃圾回收器在進(jìn)行垃圾回收的時(shí)候,為了保證垃圾回收不受干擾,是會(huì)暫停所有線程的,此時(shí)程序無(wú)法對(duì)外部的請(qǐng)求進(jìn)行響應(yīng)。(因?yàn)槟阆氚。?dāng)你在可達(dá)性分析的時(shí)候,那些引用關(guān)系還在不斷著變化,那不很難受)。
而且頻繁的垃圾回收,對(duì)于有一些程序,是很影響用戶體驗(yàn)的,例如你在玩游戲,系統(tǒng)動(dòng)不動(dòng)就停頓一下,怕你是要把這游戲給刪了。
所以說(shuō),垃圾回收是會(huì)等到內(nèi)存被使用了一定的比例的時(shí)候,才會(huì)觸發(fā)垃圾回收。至于這個(gè)比例是多少,這可能就是人為規(guī)定的了。
怎么回收?當(dāng)我們標(biāo)記好了哪些是垃圾,想要進(jìn)行回收的時(shí)候,該怎么回收比較好呢?
可能有一些人就覺得奇怪,這還不簡(jiǎn)單,看見它是垃圾,直接回收不就得了。
其實(shí)這也不無(wú)道理,簡(jiǎn)單粗暴,直接回收。
是的,確實(shí)有這樣的算法,看哪些是被我們標(biāo)記的垃圾,看見了就直接回收。這種算法我們稱之為標(biāo)記--清除算法。
標(biāo)記-清除算法工作原理:就是先標(biāo)記出所有需要回收的對(duì)象,然后在統(tǒng)一回收所有被標(biāo)記過(guò)的對(duì)象。
不過(guò),那些人你可別得意啊,因?yàn)檫@種方法雖然簡(jiǎn)單暴力,但它有個(gè)致命的缺點(diǎn)就是:
標(biāo)記清除過(guò)后,會(huì)產(chǎn)生大量的不連續(xù)內(nèi)存碎片,如果不連續(xù)的碎片過(guò)多的話,,可能會(huì)導(dǎo)致有一些大的對(duì)象存不進(jìn)去。這樣,會(huì)導(dǎo)致下面兩個(gè)問(wèn)題:
有些內(nèi)存浪費(fèi)了。
對(duì)象存不進(jìn)去,會(huì)又一次觸發(fā)垃圾回收。
復(fù)制算法
為了解決這種問(wèn)題,另外一種算法出現(xiàn)了---復(fù)制算法。就是說(shuō),它會(huì)將可用的內(nèi)存按容量劃分成兩塊。然后每次只使用其中的一塊,當(dāng)這一塊快用完的時(shí)候,就會(huì)觸發(fā)垃圾回收,它會(huì)把還存活的對(duì)象全部復(fù)制到另外一塊內(nèi)存中去,然后把這塊內(nèi)存全部清理了。
這樣,就不會(huì)出現(xiàn)碎片問(wèn)題了。
居然幫我們解決了我們必須夸一下它:不僅幫我們解決了問(wèn)題,而且實(shí)現(xiàn)上也簡(jiǎn)單、運(yùn)行也高效。
但是(凡事都有個(gè)但是的),它也是有缺點(diǎn)的,缺點(diǎn)很明顯,發(fā)現(xiàn)了沒有。假如每次存活的對(duì)象都很少很少,那另外一塊內(nèi)存不是幾乎沒有用到?所以說(shuō),這種方法有可能導(dǎo)致另外一半內(nèi)存幾乎沒用了。內(nèi)存那么寶貴,這可是很嚴(yán)重的問(wèn)題。
優(yōu)化策略:可以告訴你,有研究顯示,其實(shí)有98%的對(duì)象都是朝生夕死的,也就是說(shuō),每次存活的對(duì)象確實(shí)很少很少。既然我們都知道存活的對(duì)象很少很少了,那我們干嘛還1:1的比例來(lái)分配?所以說(shuō),HotShot虛擬機(jī)是默認(rèn)按8:1的比例來(lái)分配的。這樣,就不會(huì)出現(xiàn)很多內(nèi)存沒用到的問(wèn)題了。
可能有人會(huì)說(shuō),萬(wàn)一占比為1/9的內(nèi)存不夠用了怎么辦?不就沒地方存那些活的對(duì)象?實(shí)際上,當(dāng)內(nèi)存不夠用時(shí),可以向其他地方借些內(nèi)存來(lái)使用,例如老年代里的內(nèi)存。
這里說(shuō)明一下新生代和老年代:說(shuō)白了,新生代就是剛剛創(chuàng)建不久的對(duì)象,而老年代是已經(jīng)活了挺久的對(duì)象。也就是說(shuō),有一些對(duì)象是確實(shí)活的比較久的,對(duì)于這種對(duì)象,我們另外給它分配內(nèi)存來(lái)養(yǎng)老,而且垃圾回收時(shí),我們不用每次都來(lái)這里查找有沒垃圾對(duì)象,因?yàn)檫@些對(duì)象是垃圾的幾率會(huì)比較小。
下面在簡(jiǎn)單介紹另外兩種算法:
標(biāo)記-整理算法:這種算法和標(biāo)記-清除算法類似,不過(guò)它把垃圾清除了之后,會(huì)讓存活的對(duì)象往一個(gè)方向靠攏,以此來(lái)整理碎片。
分代收集算法:所謂分代就是把對(duì)象分成類似上面說(shuō)的老年代和新生代,在新手代一般每次垃圾回收時(shí)死的對(duì)象一般都會(huì)比較多,而老年代會(huì)比較少,基于這種關(guān)系,我們就可以采取不同的算法來(lái)針對(duì)了。
總結(jié)下垃圾回收的幾種算法:
標(biāo)記-清除算法。
復(fù)制算法。
標(biāo)記-整理算法。
分代收集算法。
最后給大家?guī)追N垃圾回收器對(duì)于垃圾的回收,你是想一邊運(yùn)行程序其他代碼一邊進(jìn)行垃圾回收?還是想把垃圾全收好再來(lái)執(zhí)行程序的其他代碼?雖然說(shuō)最終使用cpu的時(shí)間是一樣,但兩種方式還是有區(qū)別的。
下面簡(jiǎn)單介紹幾種垃圾回收器,看看他們都使用哪種方。
(1).Serial收集器
serial(串行),看這個(gè)英文單詞就知道這是一個(gè)單線程收集器。也就是說(shuō),它在進(jìn)行垃圾回收時(shí),必須暫停其他所有線程。顯然,有時(shí)垃圾回收停頓的比較久的話,這對(duì)于用戶來(lái)說(shuō)是很難受的。
(2).ParNew
這個(gè)收集器和Serial很類似,進(jìn)行垃圾回收的時(shí)候,也是得暫停其他所有線程,不過(guò),它可以多條線程工作進(jìn)行垃圾回收。
(3).Parallel Scavenge收集器
parallel,并行的意思。也是可以多線程進(jìn)行垃圾回收處理,但是它與ParNew不同。它會(huì)嚴(yán)格控制垃圾回收的時(shí)間與執(zhí)行其他代碼的時(shí)間之間的比例。我們來(lái)看一個(gè)名詞:吞吐量。
吞吐量 = 運(yùn)行用戶代碼時(shí)間 / (運(yùn)行用戶代碼時(shí)間 + 垃圾收集時(shí)間)。
也就是說(shuō),Parallet Scavenge收集器會(huì)嚴(yán)格控制吞吐量,至于這個(gè)吞吐量是多少,這個(gè)可以人為設(shè)置。
下面兩個(gè)收集器重點(diǎn)介紹下(4).CMS(Concurrent Mark Sweep)收集器
CMS收集器是基于“標(biāo)記-清除”算法實(shí)現(xiàn)的,它的運(yùn)作過(guò)程相對(duì)于前面幾種收集器來(lái)說(shuō)要更復(fù)雜一些,整個(gè)過(guò)程分為4個(gè)步驟,包括:
初始標(biāo)記(CMS initial mark)
并發(fā)標(biāo)記(CMS concurrent mark)
重新標(biāo)記(CMS remark)
并發(fā)清除(CMS concurrent sweep)
其中初始標(biāo)記、重新標(biāo)記這兩個(gè)步驟仍然需要暫停其他線程。但另外兩個(gè)步驟可以和其他線程并發(fā)執(zhí)行。初始標(biāo)記僅僅只是標(biāo)記一下GCRoots能直接關(guān)聯(lián)到的對(duì)象,速度很快,并發(fā)標(biāo)記階段就是進(jìn)行GC Roots Tracing的過(guò)程 (說(shuō)白了就是把整個(gè)圖都遍歷了,找出沒有的對(duì)象),
而重新標(biāo)記階段則是為了修正并發(fā)標(biāo)記期間,因用戶程序繼續(xù)運(yùn)作而導(dǎo)致標(biāo)記產(chǎn)生變動(dòng)的那一部分對(duì)象的標(biāo)記記錄,這個(gè)階段的停頓時(shí)間一般會(huì)比初始標(biāo)記階段稍長(zhǎng)一些,但遠(yuǎn)比并發(fā)標(biāo)記的時(shí)間短。
由于整個(gè)過(guò)程中耗時(shí)最長(zhǎng)的并發(fā)標(biāo)記和并發(fā)清除過(guò)程中,收集器線程都可以與用戶線程一起工作,所以總體上來(lái)說(shuō),CMS收集器的內(nèi)存回收過(guò)程幾乎是與與用戶線程一起并發(fā)地執(zhí)行。
(5).G1收集器
這個(gè)估計(jì)是最牛的收集器了。該收集器具有如下特點(diǎn):
并行與并發(fā):G1能充分利用現(xiàn)代計(jì)算器多CPU,多核的硬件優(yōu)勢(shì),可以使用并發(fā)或并行的方式來(lái)縮短讓其他線程暫停的優(yōu)勢(shì)。
分代收集:就是類似像分出新生代和老年代那樣處理。
空間整合:采用了復(fù)制算法+標(biāo)記-整合算法的特點(diǎn)來(lái)回收垃圾。就是整體采用標(biāo)記-整理算法,局部采用復(fù)制算法。
可預(yù)測(cè)停頓:這個(gè)就牛了,就是說(shuō),它能讓使用者明確指定在一個(gè)長(zhǎng)度為M毫秒的時(shí)間片段內(nèi),消耗在垃圾收集上的時(shí)間不超過(guò)N毫秒。
它的執(zhí)行過(guò)程大體如下:
初始標(biāo)記。
并發(fā)標(biāo)記。
最終標(biāo)記。
篩選回收。
這個(gè)流程和CMS很相似,它也是在初始標(biāo)記和最終標(biāo)記需要暫停其他線程,但其他兩個(gè)過(guò)程就可以和其他線程并發(fā)執(zhí)行。
剛才我們說(shuō)了G1收集器哪些優(yōu)點(diǎn),例如可預(yù)測(cè)停頓,這也使得篩選回收,是可以預(yù)測(cè)停頓垃圾回收的時(shí)間的,也就是說(shuō),停頓的時(shí)間是用戶自己可以控制的,這也使得一般情況下,在篩選回收的時(shí)候,我們會(huì)暫停其他線程的執(zhí)行,把所有時(shí)間都用到篩選回收上。
本次講解到這里。
完
關(guān)注公我的眾號(hào):苦逼的碼農(nóng),獲取更多原創(chuàng)文章,后臺(tái)回復(fù)"禮包"送你一份特別的資源大禮包。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://specialneedsforspecialkids.com/yun/76844.html
摘要:而使用虛擬機(jī)是實(shí)現(xiàn)這一特點(diǎn)的關(guān)鍵。每個(gè)字節(jié)碼指令都由一個(gè)字節(jié)的操作碼和附加的操作數(shù)組成。字節(jié)碼可以通過(guò)以下兩種方式轉(zhuǎn)換成合適的語(yǔ)言解釋器一條一條地讀取,解釋并執(zhí)行字節(jié)碼執(zhí)行,所以它可以很快地解釋字節(jié)碼,但是執(zhí)行起來(lái)會(huì)比較慢。 一、什么是JVM JVM是Java Virtual Machine(Java 虛擬機(jī))的縮寫,JVM是一種用于計(jì)算設(shè)備的規(guī)范,它是一個(gè)虛構(gòu)出來(lái)的計(jì)算機(jī),是通過(guò)在實(shí)...
摘要:是目前的實(shí)驗(yàn)收集器。也是需要暫停程序一切的工作,然后多線程執(zhí)行垃圾回收。與最大的不同,它關(guān)注的是垃圾回收的吞吐量。這里的吞吐量指的是總時(shí)間與垃圾回收時(shí)間的比例。篩選回收,評(píng)估標(biāo)記垃圾,根據(jù)模式回收垃圾。 《對(duì)象搜索算法與回收算法》介紹了垃圾回收的基礎(chǔ)算法,相當(dāng)于垃圾回收的方法論。接下來(lái)就詳細(xì)看看垃圾回收的具體實(shí)現(xiàn)。 上文提到過(guò)現(xiàn)代的商用虛擬機(jī)的都是采用分代收集的,不同的區(qū)域用不同的收集...
垃圾回收(GC)是JVM的一大殺器,它使程序員可以更高效地專注于程序的開發(fā)設(shè)計(jì),而不用過(guò)多地考慮對(duì)象的創(chuàng)建銷毀等操作。但是這并不是說(shuō)程序員不需要了解GC。GC只是Java編程中一項(xiàng)自動(dòng)化工具,任何一個(gè)工具都有它適用的范圍,當(dāng)超出它的范圍的時(shí)候,可能它將不是那么自動(dòng),而是需要人工去了解與適應(yīng)地適用。 擁有一定工作年限的程序員,在工作期間肯定會(huì)經(jīng)常碰到像內(nèi)存溢出、內(nèi)存泄露、高并發(fā)的場(chǎng)景。這時(shí)候在應(yīng)對(duì)這...
摘要:從版本開始,不再單獨(dú)發(fā)布或者版本了,有需要的可以自己通過(guò)去定制官方解讀官方細(xì)項(xiàng)解讀穩(wěn)步推進(jìn)系列六的小試牛刀一文讀懂的為何如此高效棄用引擎 Java語(yǔ)言特性系列 Java5的新特性 Java6的新特性 Java7的新特性 Java8的新特性 Java9的新特性 Java10的新特性 Java11的新特性 Java12的新特性 Java13的新特性 序 本文主要講述一下Java11的新...
摘要:此外,從結(jié)果我們可以得知,一個(gè)堆對(duì)象的放在局部變量表中的第一項(xiàng)引用會(huì)永遠(yuǎn)存在,在方法體內(nèi)可以將引用賦值給其他變量,這樣堆中對(duì)象就可以被其他變量所引用,即不會(huì)被回收。 原創(chuàng)不易,如需轉(zhuǎn)載,請(qǐng)注明出處https://www.cnblogs.com/baixianlong/p/10697554.html,多多支持哈! 一、什么是GC? GC是垃圾收集的意思,內(nèi)存處理是編程人員容易出現(xiàn)問(wèn)題的地...
閱讀 3080·2021-11-24 10:47
閱讀 3831·2021-11-02 14:43
閱讀 2228·2021-09-26 10:15
閱讀 2253·2021-09-08 09:35
閱讀 560·2019-08-30 12:45
閱讀 2781·2019-08-29 17:04
閱讀 3214·2019-08-26 14:05
閱讀 1259·2019-08-26 12:10