您好,登錄后才能下訂單哦!
這篇文章主要講解了“java垃圾收集器與內(nèi)存分配策略是什么”,文中的講解內(nèi)容簡(jiǎn)單清晰,易于學(xué)習(xí)與理解,下面請(qǐng)大家跟著小編的思路慢慢深入,一起來研究和學(xué)習(xí)“java垃圾收集器與內(nèi)存分配策略是什么”吧!
java虛擬機(jī)一個(gè)老生常談的問題就是垃圾回收和內(nèi)存分配。java虛擬機(jī)內(nèi)存的自動(dòng)化管理最終歸結(jié)為自動(dòng)的解決兩個(gè)問題:給對(duì)象分配內(nèi)存,以及回收分配給對(duì)象的內(nèi)存。
先說回收分配給對(duì)象的內(nèi)存吧,其中最重要也就輸入無(wú)用對(duì)象的收集了,其實(shí)也就兩步走,收集-釋放。收集有涉及到垃圾收集算法,下面具體講一下。
判定對(duì)象是否需要被回收的算法
垃圾收集算法的核心當(dāng)然是判定當(dāng)前對(duì)象是否已無(wú)用,然后才開始收集?!渡钊肜斫鈐ava虛擬機(jī)》中將了兩種收集算法。一種為引用計(jì)數(shù)算法,一種為可達(dá)性分析算法。
一.引用計(jì)數(shù)算法:
給對(duì)象中添加一個(gè)程序計(jì)數(shù)器,對(duì)象被調(diào)用一次,計(jì)數(shù)器加1,當(dāng)對(duì)象的引用失效時(shí),計(jì)數(shù)器減1.當(dāng)計(jì)數(shù)器為0 時(shí)則表示對(duì)象可能已經(jīng)無(wú)用需要被回收。
拓展兩點(diǎn):
1.當(dāng)對(duì)象被new出來的時(shí)候,如果沒有使用它,是會(huì)被回收的,也就是說創(chuàng)建初始時(shí),計(jì)數(shù)器為0。
2.當(dāng)對(duì)象引用失效時(shí),計(jì)數(shù)器減1。我記得當(dāng)時(shí)被面試問到對(duì)象引用怎么失效的,怎么判斷?,F(xiàn)在想想當(dāng)使用失效應(yīng)該不是我們控制的,當(dāng)對(duì)應(yīng)被引用之后,過一段時(shí)間,這個(gè)引用就會(huì)自動(dòng)失效吧。(這里是個(gè)人理解,有錯(cuò)誤望指正?。?/em>
雖然引用計(jì)數(shù)算法很簡(jiǎn)單明了,但是有一個(gè)嚴(yán)重的問題就是循環(huán)引用的對(duì)象無(wú)法被回收。類似下面這種:
ReferenceCountingGC objA = new ReferenceCountingGC();
ReferenceCountingGC objB = new ReferenceCountingGC();
objA.instance = objB;
objB.instance = objA;
所以出現(xiàn)了另一種垃圾收集的算法。
二.可達(dá)性分析算法:
算法的基本思想是,通過一系列的“GC Roots ”的對(duì)象作為起點(diǎn),通過這些起點(diǎn)能找到的對(duì)象則說明該對(duì)象是存活的,如果通過所有的起點(diǎn)都不能達(dá)到該對(duì)象,則說明該對(duì)象可能是無(wú)用的,會(huì)被回收。
主流的虛擬機(jī)都是通過可達(dá)性分析算法來判定對(duì)象是否要被回收的。
下面給大家看一個(gè)例子證明java虛擬機(jī)是通過可達(dá)性分析算法來判斷的。
package chapter.three;
//Debug Configuration:
//-verbose:gc: -Xms20M -Xmx20M -Xmn10M -verbose:gc -XX:+PrintGCDetails -XX:SurvivorRatio=8
public class ReferenceCountingGC {
public Object instance = null;
private static final int _1MB = 1024 * 1024;
private byte[] bigSize = new byte[2 * _1MB];
public static void testGC()
{
ReferenceCountingGC objA = new ReferenceCountingGC();
ReferenceCountingGC objB = new ReferenceCountingGC();
System.out.println("第一次");
objA.instance = objB;
objB.instance = objA;
System.out.println(objA);
System.out.println(objB);
objA = null;
objB = null;
System.gc();
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("第二次");
objA.instance = objB;
objB.instance = objA;
System.out.println(objA);
System.out.println(objB);
}
public static void main(String[] args)
{
testGC();
}
}
objA 和objB是相互引用的,如果是采用計(jì)數(shù)器的算法,兩次輸出應(yīng)該是一樣的,如果是采用可達(dá)性分析的算法,第二次會(huì)報(bào)錯(cuò),因?yàn)閮蓚€(gè)對(duì)象被回收了,這樣操作兩個(gè)對(duì)象會(huì)報(bào)錯(cuò)找不到。運(yùn)行結(jié)果。
第一次chapter.three.ReferenceCountingGC@15db9742chapter.three.ReferenceCountingGC@6d06d69c第二次Exception in thread "main" java.lang.NullPointerException at chapter.three.ReferenceCountingGC.testGC(ReferenceCountingGC.java:34) at chapter.three.ReferenceCountingGC.main(ReferenceCountingGC.java:43)
拓展講一點(diǎn)吧,其實(shí)這個(gè)不是很重要,了解一下
另外書中還講了一點(diǎn),哪些不可達(dá)的對(duì)象,也不一定就會(huì)被回收,并沒有直接判死刑,而是判的緩刑,有一次自救的機(jī)會(huì)。也就是說,所有的“GC Roots”根節(jié)點(diǎn)都無(wú)法到該對(duì)象時(shí),該對(duì)象會(huì)被標(biāo)記一次,并對(duì)其進(jìn)行一次篩選。篩選的條件是改對(duì)象有沒有重寫finalize()方法,如果沒有重寫,那么該對(duì)象就會(huì)被判死刑,會(huì)被回收。如果該對(duì)象有重寫finalize()方法,就會(huì)執(zhí)行這個(gè)方法,對(duì)象可以在這個(gè)方法中自救。但是這個(gè)方法只會(huì)條用一次,也就是說第二次如果還是不可達(dá)還是會(huì)被回收掉。不知道大家有沒有理解,下面看一個(gè)例子
package chapter.three;
public class FinalizeEscapeGC {
public static FinalizeEscapeGC SAVE_HOOK=null;
public void isAlive(){
System.out.println("yes,i am still alive...");
}
@Override
protected void finalize() throws Throwable {
super.finalize();
System.out.println("finalize method executed !");
FinalizeEscapeGC.SAVE_HOOK=this;
}
public static void main(String[] args) throws InterruptedException {
SAVE_HOOK=new FinalizeEscapeGC();
System.out.println("first:");
SAVE_HOOK=null;
System.gc();
Thread.sleep(500);
if(SAVE_HOOK !=null){
SAVE_HOOK.isAlive();
}else {
System.out.println("no,i am dead...");
}
System.out.println("second:");
SAVE_HOOK=null;
System.gc();
Thread.sleep(500);
if(SAVE_HOOK !=null){
SAVE_HOOK.isAlive();
}else {
System.out.println("no,i am dead...");
}
}
}
結(jié)果
first:
finalize method executed !
yes,i am still alive...
second:
no,i am dead...
但是書中建議我們最好不要這么做,因?yàn)閒inalize()能做的,try-finally語(yǔ)句或者其他的語(yǔ)句就能做,,且做的更好,所以我這也是簡(jiǎn)單的提一下。
垃圾收集算法
有4種,標(biāo)記-清除算法,復(fù)制算法,標(biāo)記整理算法,分代收集算法。其實(shí)也就三種,以為分代收集算就是不同場(chǎng)景使用前面的三種方法。
標(biāo)記清除算法 :就是先將需要回收的內(nèi)存標(biāo)記,然后清除掉。這種算法思路簡(jiǎn)單,但是會(huì)造成內(nèi)存分布零散,存儲(chǔ)大文件會(huì)導(dǎo)致觸發(fā)一次GC.
復(fù)制算法:是將內(nèi)存按照容量劃分成大小相等的兩塊,每次都只使用其中的一塊,其中一塊內(nèi)存使用完了,就會(huì)統(tǒng)計(jì)出這塊內(nèi)存中存活的對(duì)象復(fù)制到另一塊內(nèi)存中,并且清理當(dāng)前快,這樣循環(huán)使用。但是這樣的缺點(diǎn)就是導(dǎo)師內(nèi)存使用率太低,總有一半的內(nèi)存在閑置狀態(tài)。
標(biāo)記整理的算法,標(biāo)記整理算法也是先將需要回收的內(nèi)存進(jìn)行整理,然后并不是直接清理掉,而是將存活的對(duì)象向前移動(dòng),保存內(nèi)存的前面都是存活的對(duì)象,然后清理掉回收的對(duì)象。這樣做的解決了標(biāo)記清除算法導(dǎo)致的內(nèi)存可用空間分布的太散的問題,但是這樣做的缺點(diǎn)就是效率低下。
分代收集算法:是把java堆分為老年代,新生代。新生代中對(duì)象存活時(shí)間普遍短使用復(fù)制的算法,老年代對(duì)象周期較長(zhǎng),采用標(biāo)記整理算法或者標(biāo)記清除算法。
垃圾收集器
上面的算法部分都是回收的理論,現(xiàn)在垃圾收集器則是內(nèi)存回收的具體實(shí)現(xiàn)啦,虛擬機(jī)中存在很多的收集器,并且這些收集器是配合使用的,各種收集器有各自的優(yōu)缺點(diǎn),下圖是HotSpot虛擬機(jī)中使用的垃圾收集器,以及這些收集器之間哪些是可以搭配只用的。
serial收集器:是一個(gè)單線程收集器,只有一個(gè)cpu或者一條收集線程去進(jìn)行收集,而且更重要的是在收集線程在進(jìn)行垃圾收集的過程中會(huì)暫停用所有的用戶線程,導(dǎo)致用戶進(jìn)行等待。
ParNew收集器:ParNew收集器是serial收集器的多線程版本,但是一樣的,在收集線程工作時(shí),所有的用戶線程會(huì)進(jìn)行等待。
parallel Scavenge收集器 是一個(gè)新生代收集器,使用的復(fù)制算法,是并行的多線程。這個(gè)收集器關(guān)注點(diǎn)是吞吐量,吞吐量是指,虛擬機(jī)總共運(yùn)行100s,垃圾收集占用1s,那么吞吐量就是99%。
se'rial Old 收集器 是serial收集器的老年代版本,采用的是標(biāo)記整理的算法。一樣是一個(gè)單線程收集器。
parallel Old 收集器是parallel Scavenge收集器的老年代版本,采用的是標(biāo)記整理的算法,多線程。
GMS收集器:是一種獲取最短回收停頓時(shí)間為目標(biāo)的收集器。是基于標(biāo)記清除 的算法實(shí)現(xiàn)的。有4個(gè)過程:
初始標(biāo)記
并發(fā)標(biāo)記
重新標(biāo)記
并發(fā)清除
其中 初始標(biāo)記和重新標(biāo)記兩部依然會(huì)“stop the world”也就是說這兩部還是會(huì)造成用戶線程等待,但是相對(duì)而言等待的時(shí)間要短很多。
G1收集器,是一種面向服務(wù)器的收集器,具備以下特點(diǎn):
并行與并發(fā)
分代收集
空間整合
可預(yù)測(cè)的停頓
G1收集器大致分為以下幾個(gè)步驟
初始標(biāo)記
并發(fā)標(biāo)記
最終標(biāo)記
篩選回收
內(nèi)存分配與回收策略
對(duì)象優(yōu)先在eden(新生代)中分配。
大對(duì)象直接進(jìn)入老年代
長(zhǎng)期存活的對(duì)象進(jìn)入老年代
動(dòng)態(tài)對(duì)象年齡判定
感謝各位的閱讀,以上就是“java垃圾收集器與內(nèi)存分配策略是什么”的內(nèi)容了,經(jīng)過本文的學(xué)習(xí)后,相信大家對(duì)java垃圾收集器與內(nèi)存分配策略是什么這一問題有了更深刻的體會(huì),具體使用情況還需要大家實(shí)踐驗(yàn)證。這里是億速云,小編將為大家推送更多相關(guān)知識(shí)點(diǎn)的文章,歡迎關(guān)注!
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。