對(duì)象被判定為垃圾的標(biāo)準(zhǔn):
十多年的密山網(wǎng)站建設(shè)經(jīng)驗(yàn),針對(duì)設(shè)計(jì)、前端、開(kāi)發(fā)、售后、文案、推廣等六對(duì)一服務(wù),響應(yīng)快,48小時(shí)及時(shí)工作處理。成都全網(wǎng)營(yíng)銷的優(yōu)勢(shì)是能夠根據(jù)用戶設(shè)備顯示端的尺寸不同,自動(dòng)調(diào)整密山建站的顯示方式,使網(wǎng)站能夠適用不同顯示終端,在瀏覽器中調(diào)整網(wǎng)站的寬度,無(wú)論在任何一種瀏覽器上瀏覽網(wǎng)站,都能展現(xiàn)優(yōu)雅布局與設(shè)計(jì),從而大程度地提升瀏覽體驗(yàn)。創(chuàng)新互聯(lián)從事“密山網(wǎng)站設(shè)計(jì)”,“密山網(wǎng)站推廣”以來(lái),每個(gè)客戶項(xiàng)目都認(rèn)真落實(shí)執(zhí)行。
判定對(duì)象是否為垃圾的算法:
可達(dá)性分析算法遍歷引用鏈如圖:
可以作為GC Root的對(duì)象:
光有垃圾標(biāo)記算法還不行,JVM還需要有垃圾回收算法來(lái)將這些標(biāo)記為垃圾的對(duì)象給釋放回收掉。主要的回收算法有以下幾種:
1.標(biāo)記 - 清除算法(Mark and Sweep):
缺點(diǎn):由于標(biāo)記 - 清除不需要進(jìn)行對(duì)象的移動(dòng),并且僅對(duì)不可達(dá)的對(duì)象進(jìn)行處理,因此使用該回收算法之后會(huì)產(chǎn)生大量不連續(xù)的內(nèi)存碎片。而內(nèi)存碎片過(guò)多可能會(huì)導(dǎo)致以后在程序運(yùn)行過(guò)程中,需要分配內(nèi)存給較大的對(duì)象時(shí),無(wú)法找到足夠大的連續(xù)內(nèi)存空間,從而不得不再次觸發(fā)垃圾回收工作,若依舊無(wú)法分配內(nèi)存的話就會(huì)觸發(fā)內(nèi)存溢出異常。
1.復(fù)制算法(Copying):
優(yōu)點(diǎn):解決內(nèi)存碎片化問(wèn)題,順序分配內(nèi)存,簡(jiǎn)單高效。該算法適用于對(duì)象存活率低的場(chǎng)景,所以普遍應(yīng)用在新生代中,因?yàn)樾律锏膶?duì)象存活率通常情況下只有10%左右
3.標(biāo)記 - 整理算法(Compacting):
優(yōu)點(diǎn):避免了標(biāo)記 - 清除算法所帶來(lái)的內(nèi)存不連續(xù)性問(wèn)題,以及不需要像復(fù)制算法那樣需要設(shè)置兩塊內(nèi)存互換。該算法適用于對(duì)象存活率較高的場(chǎng)景,所以普遍應(yīng)用在老年代中,因?yàn)槔夏甏飳?duì)象存活率較高
4.分代收集算法(Generational Collector):
在JDK7及之前的JVM版本共有三個(gè)分代,即新生代、老年代和永久代(注意,永久代不存在于堆中,而是存在于方法區(qū)):
而JDK8及以后的版本只有新生代和老年代:
分代收集算法的GC分為兩種:
新生代用于盡可能快速地收集掉那些生命周期短的對(duì)象,新生代分為兩個(gè)區(qū):
對(duì)象如何晉升到老年代:
常用的調(diào)優(yōu)參數(shù):
綜上,老年代用于存放生命周期較長(zhǎng)的對(duì)象,老年代采用的是標(biāo)記 - 整理算法。
Full GC和Major GC:
觸發(fā)Full GC的條件:
注:promotion failed是在進(jìn)行Minor GC時(shí),survivor space放不下、對(duì)象只能放入老年代,而此時(shí)老年代也放不下造成的;concurrent mode failure是在執(zhí)行CMS GC的過(guò)程中同時(shí)有對(duì)象要放入老年代,而此時(shí)老年代空間不足造成的。
在了解垃圾收集器之前,我們需要知道一個(gè)概念“Stop-the-World”:
除此之外,我們需要知道什么是Safepoint:
JVM的運(yùn)行模式:
各垃圾收集器之間的聯(lián)系,即可以搭配使用關(guān)系:
Serial收集器(啟動(dòng)參數(shù):-XX:+UseSerialGC,采用復(fù)制算法):
ParNew收集器(啟動(dòng)參數(shù):-XX:+UseParNewGC,采用復(fù)制算法):
Parallel Scavenge收集器(啟動(dòng)參數(shù):-XX:+UseParallelGC,采用復(fù)制算法):
Serial Old收集器(啟動(dòng)參數(shù):-XX:+UseSerialOldGC,采用標(biāo)記 - 整理算法):
Parallel Old收集器(啟動(dòng)參數(shù):-XX:+UseParallelOldGC,采用標(biāo)記 - 整理算法):
CMS收集器(啟動(dòng)參數(shù):-XX:+UseConcMarkSweepGC,采用標(biāo)記 - 清除算法):
CMS收集器收集流程:
CMS收集器圖示:
G1收集器(啟動(dòng)參數(shù):-XX:+UseG1GC,采用復(fù)制 + 標(biāo)記 - 整理算法):
1.Object的finalize()方法的作用是否與C++的析構(gòu)函數(shù)作用相同:
示例代碼:
package com.example.demo.gc;
/**
* @author 01
* @date 2019-07-18
**/
public class Finalization {
public static Finalization finalization;
@Override
protected void finalize() throws Throwable {
System.out.println("finalize");
finalization = this;
}
public static void main(String[] args) {
Finalization f = new Finalization();
System.out.println("First print: " + f);
f = null;
System.gc();
System.out.println("Second print: " + f);
System.out.println(f.finalization);
}
}
執(zhí)行結(jié)果:
從執(zhí)行結(jié)果可以看到,F(xiàn)inalization對(duì)象被GC回收時(shí)finalize()方法會(huì)被調(diào)用,finalize()方法里將當(dāng)前對(duì)象this賦值給了靜態(tài)屬性finalization實(shí)現(xiàn)了對(duì)象的“重生”,所以在GC之后依舊能打印到該對(duì)象的地址信息
注:finalize是個(gè)不太可控的方法因此并不常用,并且在JDK9+版本被標(biāo)注為過(guò)時(shí)方法
2.Java中的強(qiáng)引用,軟引用,弱引用及虛引用有什么用:
所謂強(qiáng)引用,就是我們最常見(jiàn)的普通對(duì)象引用,只要還有強(qiáng)引用指向一個(gè)對(duì)象,就能表明對(duì)象還“活著”,垃圾收集器不會(huì)碰這種對(duì)象。對(duì)于一個(gè)普通的對(duì)象,如果沒(méi)有其他的引用關(guān)系,只要超過(guò)了引用的作用域或者顯式地將相應(yīng)(強(qiáng))引用賦值為 null,就是可以被垃圾收集的了,當(dāng)然具體回收時(shí)機(jī)還是要看垃圾收集策略??偨Y(jié):
- 最普遍的引用,例:
Object obj = new Object();
- JVM寧可拋出OutOfMemoryError終止程序也不會(huì)回收具有強(qiáng)引用的對(duì)象
- 通過(guò)將對(duì)象設(shè)置為null來(lái)弱化引用,使其被回收
是一種相對(duì)強(qiáng)引用弱化一些的引用,可以讓對(duì)象豁免一些垃圾收集,只有當(dāng) JVM 認(rèn)為內(nèi)存不足時(shí),才會(huì)去試圖回收軟引用指向的對(duì)象。JVM 會(huì)確保在拋出 OutOfMemoryError 之前,清理軟引用指向的對(duì)象。軟引用通常用來(lái)實(shí)現(xiàn)內(nèi)存敏感的緩存,如果還有空閑內(nèi)存,就可以暫時(shí)保留緩存,當(dāng)內(nèi)存不足時(shí)清理掉,這樣就保證了使用緩存的同時(shí),不會(huì)耗盡內(nèi)存。總結(jié):
- 對(duì)象處在有用但非必須的狀態(tài)
- 只有當(dāng)內(nèi)存空間不足時(shí),GC會(huì)回收該引用的對(duì)象內(nèi)存
- 軟引用通常用來(lái)實(shí)現(xiàn)內(nèi)存敏感的高速緩存
- 可以配合引用對(duì)象使用(ReferenceQueue)
弱引用并不能使對(duì)象豁免垃圾收集,僅僅是提供一種訪問(wèn)在弱引用狀態(tài)下對(duì)象的途徑。這就可以用來(lái)構(gòu)建一種沒(méi)有特定約束的關(guān)系,比如,維護(hù)一種非強(qiáng)制性的映射關(guān)系,如果試圖獲取時(shí)對(duì)象還在,就使用它,否則重現(xiàn)實(shí)例化。它同樣是很多緩存實(shí)現(xiàn)的選擇??偨Y(jié):
- 用于描述非必須的對(duì)象,比軟引用更弱一些
- 發(fā)生GC時(shí)就會(huì)被回收掉,不過(guò)被回收的概率也不大,因?yàn)镚C線程優(yōu)先級(jí)比較低
- 適用于引用偶爾被使用且不影響垃圾收集的對(duì)象
- 可以配合引用對(duì)象使用(ReferenceQueue)
對(duì)于虛引用,你不能通過(guò)它訪問(wèn)對(duì)象。虛引用僅僅是提供了一種確保對(duì)象被 finalize 以后,做某些事情的機(jī)制,比如,通常用來(lái)做所謂的 Post-Mortem 清理機(jī)制,例如 Java 平臺(tái)自身 Cleaner 機(jī)制等,也有人利用幻象引用監(jiān)控對(duì)象的創(chuàng)建和銷毀??偨Y(jié):
- 不會(huì)決定對(duì)象的生命周期
- 虛引用的對(duì)象任何時(shí)候都可能被垃圾收集器回收,就像是沒(méi)有引用的對(duì)象一樣
- 虛引用通常用來(lái)跟蹤對(duì)象被垃圾收集器回收的活動(dòng),起哨兵作用
- 與軟引用和弱引用不同的是,該引用必須與引用對(duì)列(ReferenceQueue)聯(lián)合使用
軟引用代碼示例:
// 強(qiáng)引用
String str = new String("abc");
// 轉(zhuǎn)換為軟引用
SoftReference softReference = new SoftReference<>(str);
弱引用代碼示例:
String str = new String("abc");
// 弱引用
WeakReference weakReference = new WeakReference<>(str);
虛引用代碼示例:
String str = new String("abc");
// 引用隊(duì)列
ReferenceQueue queue = new ReferenceQueue<>();
// 轉(zhuǎn)換為虛引用
PhantomReference phantomReference = new PhantomReference<>(str, queue);
// GC在回收一個(gè)對(duì)象時(shí),如果發(fā)現(xiàn)該對(duì)象存在虛引用,那么在回收之前會(huì)先將該對(duì)象的虛引用添加到與該對(duì)象關(guān)聯(lián)的引用隊(duì)列中;程序代碼可以通過(guò)判斷引用隊(duì)列是否已加入虛引用來(lái)得知被引用的對(duì)象是否已經(jīng)被回收
引用隊(duì)列(ReferenceQueue):
引用強(qiáng)度關(guān)系:
下面流程圖簡(jiǎn)單總結(jié)了對(duì)象生命周期和不同可達(dá)性狀態(tài),以及不同狀態(tài)可能的改變關(guān)系:
上圖的具體狀態(tài),實(shí)際是 Java 定義的不同可達(dá)性級(jí)別(reachability level),在之前也說(shuō)過(guò)判斷對(duì)象可達(dá)性,是 JVM 垃圾收集器決定如何處理對(duì)象的一部分考慮。可達(dá)性具體含義如下:
各引用包裝類的繼承關(guān)系圖:
下面我們來(lái)用一個(gè)例子演示引用包裝對(duì)象及引用隊(duì)列的使用,首先定義一個(gè)普通的類,并且實(shí)現(xiàn)finalize方法以便我們?cè)跍y(cè)試時(shí)可以看到該對(duì)象是否被GC回收了:
package com.example.demo.gc;
/**
* @author 01
* @date 2019-07-18
**/
public class NormalObject {
public String name;
public NormalObject(String name) {
this.name = name;
}
@Override
protected void finalize() throws Throwable {
System.out.println("finalizing obj: " + name);
}
}
然后定義一個(gè)WeakReference的子類,目的是擴(kuò)展name屬性,以便我們?cè)跍y(cè)試時(shí)能夠得知是哪個(gè)對(duì)象的引用對(duì)象:
package com.example.demo.gc;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.WeakReference;
/**
* @author 01
* @date 2019-07-18
**/
public class NormalObjectWeakReference extends WeakReference {
public String name;
public NormalObjectWeakReference(NormalObject referent, ReferenceQueue queue) {
super(referent, queue);
this.name = referent.name;
}
@Override
protected void finalize() throws Throwable {
System.out.println("finalizing NormalObjectWeakReference: " + name);
}
}
最后編寫一個(gè)測(cè)試類:
package com.example.demo.gc;
import java.lang.ref.Reference;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.List;
/**
* @author 01
* @date 2019-07-18
**/
public class ReferenceQueueTests {
// 引用隊(duì)列
private static ReferenceQueue queue = new ReferenceQueue<>();
/**
* 檢查引用隊(duì)列里有沒(méi)有引用對(duì)象,有的話則打印相關(guān)信息
*/
private static void checkQueue() {
Reference extends NormalObject> reference;
while ((reference = queue.poll()) != null) {
// 存在于引用隊(duì)列中的引用對(duì)象
System.out.println("In Queue: " + ((NormalObjectWeakReference) (reference)).name);
// 獲取引用的對(duì)象實(shí)例
System.out.println("reference object: " + reference.get());
}
}
public static void main(String[] args) throws InterruptedException {
List> weakReferenceList = new ArrayList<>();
// 創(chuàng)建引用對(duì)象
for (int i = 0; i < 3; i++) {
NormalObject normalObject = new NormalObject("Weak-" + i);
NormalObjectWeakReference reference = new NormalObjectWeakReference(normalObject, queue);
weakReferenceList.add(reference);
System.out.println("Create weak: " + reference);
}
System.out.println("\nbefore gc ------");
checkQueue();
System.out.println("\ngc ing... ------");
System.gc();
// 讓線程休眠一會(huì),以保gc能夠正常執(zhí)行完畢
Thread.sleep(1000);
System.out.println("\nafter gc ------");
checkQueue();
}
}
運(yùn)行結(jié)果:
可以看到在GC執(zhí)行之前調(diào)用checkQueue方法沒(méi)有打印任何信息,因?yàn)榇藭r(shí)引用隊(duì)列中沒(méi)有任何引用對(duì)象。而當(dāng)GC執(zhí)行之后,引用隊(duì)列中就被添加了與之相關(guān)聯(lián)的引用對(duì)象,所以就能夠打印出引用對(duì)象的相關(guān)信息
GC相關(guān)參考文章: