1.垃圾回收機制
(1)什么是垃圾回收機制(Garbage Collection, 簡稱GC)
- 指自動管理動態(tài)分配的內(nèi)存空間的機制,自動回收不再使用的內(nèi)存,以避免內(nèi)存泄漏和內(nèi)存溢出的問題
- 最早是在1960年代提出的,程序員需要手動管理內(nèi)存的分配和釋放
- 這往往會導(dǎo)致內(nèi)存泄漏和內(nèi)存溢出等問題,同時也增加了程序員的工作量,特別是C++/C語言開發(fā)的時候
- Java語言是最早實現(xiàn)垃圾回收機制的語言之一,其他編程語言,如C#、Python和Ruby等,也都提供了垃圾回收機制
(2)JVM自動垃圾回收機制
-
指Java虛擬機在運行Java程序時,自動回收不再使用的對象所占用的內(nèi)存空間的過程
-
Java程序中的對象,一旦不再被引用會被標(biāo)記為垃圾對象,JVM會在適當(dāng)?shù)臅r候自動回收這些垃圾對象所占用的內(nèi)存空間
-
優(yōu)點
- 減少了程序員的工作量,不需要手動管理內(nèi)存
- 動態(tài)地管理內(nèi)存,根據(jù)應(yīng)用程序的需要進行分配和回收,提高了內(nèi)存利用率
- 避免內(nèi)存泄漏和野指針等問題,增加程序的穩(wěn)定性和可靠
-
缺點
- 垃圾回收會占用一定的系統(tǒng)資源,可能會影響程序的性能
- 垃圾回收過程中會停止程序的執(zhí)行,可能會導(dǎo)致程序出現(xiàn)卡頓等問題
- 不一定能夠完全解決內(nèi)存泄漏等問題,需要在編寫代碼時注意內(nèi)存管理和編碼規(guī)范
(3)引用計數(shù)法
- 基本思想,跟蹤每個對象被引用的次數(shù),當(dāng)引用次數(shù)為0時,就可以將該對象回收
- 在JVM中,每個對象都有一個引用計數(shù)器,當(dāng)對象被引用時,引用計數(shù)器+1
- 當(dāng)對象被取消引用時,引用計數(shù)器-1
- 當(dāng)引用計數(shù)器為0時,該對象就可以被回收
- 優(yōu)點
- 實現(xiàn)簡單,回收垃圾的效率高
- 缺點
- 循環(huán)引用無法回收。如果兩個對象互相引用,它們的引用計數(shù)器永遠不會為0,因此無法被回收
- 引用計數(shù)器開銷大,每個對象都需要一個引用計數(shù)器,如果對象很多,開銷就會很大
public class Main {
public static void main(String[] args) {
A a = new A();
B b = new B();
a.setB(b);
b.setA(a);
a = null;
b = null;
System.gc();
}
}
class A {
private B b;
public void setB(B b) {
this.b = b;
}
}
class B {
private A a;
public void setA(A a) {
this.a = a;
}
}
- 類A和類B相互引用,每個對象都持有對方的引用,形成了一個循環(huán)引用的環(huán),當(dāng)Main方法執(zhí)行完畢后,a和b對象都置為null
- 由于它們相互引用,它們的引用計數(shù)器都不為0,無法被垃圾回收器回收,導(dǎo)致內(nèi)存泄漏
- 但是上面代碼卻不會發(fā)生內(nèi)存泄漏,因為多數(shù)jvm沒有采用這個引用計數(shù)器方案,而是采用可達性分析算法
(4)可達性分析算法
- 可達性分析算法的基本思想是通過一系列的“GC Roots”對象作為起點進行搜索。
- 如果“GC Roots”和一個對象之間沒有可達路徑,則稱該對象是不可達的,不過要注意的是被判定為不可達的對象不一定就會成為可回收對象。
- 被判定為不可達的對象要成為回收對象,要至少經(jīng)歷兩次標(biāo)記過程。
- 如果在這兩次標(biāo)記過程中仍然沒有逃脫成為可回收對象的可能性,則基本上就真的成為可回收對象了。
通過一系列稱為“GC Roots”的對象作為起始點,從這些節(jié)點開始向下搜索,搜索走過的路徑稱為“引用鏈”,當(dāng)一個對象到 GC Roots 沒有任何的引用鏈相連時(從 GC Roots 到這個對象不可達)時,證明此對象不可用。
(5)什么是GC Root
- 指一些被JVM認(rèn)為是存活的對象,它們是垃圾回收算法的起點
- 可以理解為由堆外指向堆內(nèi)的引用, 本身是沒有存儲位置,都是字節(jié)碼加載運行過程中加入 JVM 中的一些普通引用
- 通俗的例子可以是一個樹形結(jié)構(gòu),樹的根節(jié)點就是GC Roots
- 是垃圾回收器的起點,如果一個節(jié)點沒有任何子節(jié)點與根節(jié)點相連,那這個節(jié)點就被認(rèn)為是不可達的,可以被回收器回收
(6)JVM中的GCRoots對象有哪幾種
-
虛擬機棧(棧幀中的本地變量表)中引用的對象。
-
方法區(qū)中類靜態(tài)屬性引用的對象
- JDK 1.7 開始靜態(tài)變量的存儲從方法區(qū)移動到堆中
- 比如你定義了一個static 的集合對象,那里面添加的對象就是可以被GC Root可達的
-
方法區(qū)中常量引用的對象
- 字符串常量池從 JDK 1.7 開始由方法區(qū)移動到堆中
-
本地方法棧中JNI(即一般說的Native方法)引用的對象。
(7)對象可回收,就一定會被回收嗎?
- 不一定會回收,對象的finalize方法給了對象一次最后一次的機會。
- 當(dāng)對象不可達(可回收)并發(fā)生 GC 時,會先判斷對象是否執(zhí)行了 finalize 方法,如果未執(zhí)行則會先執(zhí)行 finalize 方法
- 將當(dāng)前對象與 GC Roots 關(guān)聯(lián),執(zhí)行 finalize 方法之后,GC 會再次判斷對象是否可達
- 如果不可達,則會被回收,如果可達,則不回收!
- 需要注意的是 finalize 方法只會被執(zhí)行一次,如果第一次執(zhí)行 finalize 方法,對象變成了可達,則不會回收
- 但如果對象再次被 GC,則會忽略 finalize 方法,對象會被直接回收掉!
2.垃圾回收算法
(1)垃圾回收算法之標(biāo)記-清除算法
- 是一種常見的垃圾回收算法,它的基本思路是分為兩個階段:標(biāo)記階段和清除階段
- 標(biāo)記階段
- 垃圾回收器會從一些GC Roots對象開始,遍歷整個對象圖,標(biāo)記所有被引用的對象
- 被標(biāo)記的對象會被打上標(biāo)記,表示這些對象是“活”的對象,需要保留下來,未被標(biāo)記的對象就是垃圾對象,可以被回收
- 清除階段
- 垃圾回收器會對所有未被標(biāo)記的對象進行回收
- 優(yōu)點
- 能夠回收不連續(xù)的內(nèi)存空間
- 缺點
- 標(biāo)記和清除兩個步驟,都需要垃圾回收器遍歷整個對象圖,耗費時間較長
- 會產(chǎn)生內(nèi)存碎片,當(dāng)頻繁進行垃圾回收時,內(nèi)存碎片會越來越多導(dǎo)致可用內(nèi)存空間不足,從而影響程序的性能和穩(wěn)定性
- 應(yīng)用場景
- 在實際應(yīng)用中,標(biāo)記清除法一般用于不需要頻繁進行垃圾回收的場景,比如在Java堆中大對象的分配和回收
- 后續(xù)的收集算法大多都是以標(biāo)記-清除算法為基礎(chǔ),對其缺點進行改進
(2)垃圾回收算法之標(biāo)記-復(fù)制算法
- 標(biāo)記算法是一種常見的垃圾回收算法,它的基本思路是將Java堆分為兩個區(qū)域:一個活動區(qū)域和一個空閑區(qū)域
- 在垃圾回收過程中,首先標(biāo)記所有被引用的對象
- 然后將所有被標(biāo)記的對象復(fù)制到空閑區(qū)域中,最后交換兩個區(qū)域的角色,完成垃圾回收
- 標(biāo)記復(fù)制算法的詳細(xì)實現(xiàn)步驟
- 將Java堆分為兩個區(qū)域:一個活動區(qū)域和一個空閑區(qū)域,初始時,所有對象都分配在活動區(qū)域中
- 從GC Roots對象開始,遍歷整個對象圖,標(biāo)記所有被引用的對象
- 對所有被標(biāo)記存活的對象進行遍歷,將它們復(fù)制到空閑區(qū)域中,并更新所有指向它們的引用,使它們指向新的地址
- 對所有未被標(biāo)記的對象進行回收,將它們所占用的內(nèi)存空間釋放
- 交換活動區(qū)域和空閑區(qū)域的角色,空閑區(qū)域變?yōu)樾碌幕顒訁^(qū)域,原來的活動區(qū)域變?yōu)榭臻e區(qū)域
- 當(dāng)空閑區(qū)域的內(nèi)存空間不足時,進行一次垃圾回收,重復(fù)以上步驟。
-
優(yōu)點
- 如果內(nèi)存中的垃圾對象較多,需要復(fù)制的對象就較少,則效率高
- 清理后,內(nèi)存碎片少
-
缺點
- 標(biāo)記復(fù)制算法的效率較高,但是預(yù)留一半的內(nèi)存區(qū)域用來存放存活的對象,占用額外的內(nèi)存空間
- 如果出現(xiàn)存活對象數(shù)量比較多的時候,需要復(fù)制較多的對象 效率低
- 假如是在老年代區(qū)域,99%的對象都是存活的,則性能底,所以老年代不適合這個算法
-
應(yīng)用場景
-
標(biāo)記復(fù)制算法一般用于新生代的垃圾回收,因此需要對新生代的對象進行分代管理
-
虛擬機多數(shù)采用這個算法,對新生代進行內(nèi)存管理,因為多數(shù)這個新生代區(qū)域的存活對象數(shù)量少
-
國外有公司統(tǒng)計過多數(shù)業(yè)務(wù),98%撐不過一次GC; 所以不用1:1比例分配新生代的空間
-
原因
- GC時, 將Eden和Survivor中存活對象一次性復(fù)制到另外一塊Survivor空間上, 然后清理掉Eden和已用過的那塊Survivor空間
- 每次新生代中可用內(nèi)存空間為整個新生代容量的90% (Eden的80% + Survivor的 10%) ,
- 只有一個Survivor空間, 即10%的新生代是會被浪費而已
-
(3)垃圾回收算法之標(biāo)記-整理算法
- 從根節(jié)點開始對所有可達對象做一次標(biāo)記,但之后并不簡單地清理未標(biāo)記的對象,而是將所有的存活對象壓縮到內(nèi)存的一端
- 然后清理邊界外的垃圾,避免了碎片的產(chǎn)生,也不需要兩塊相同的內(nèi)存空間,因此性價比比較高
- 優(yōu)點
- 解決了標(biāo)記清除算法的碎片化的問題
- 對比標(biāo)記-復(fù)制算法,不用浪費額外的空間
- 對比 標(biāo)記-清除算法與標(biāo)記-整理算法,前者是一種非移動式的回收算法, 而后者是移動式的回收,且沒有了碎片化內(nèi)存
- 缺點
- 效率相比于標(biāo)記復(fù)制算法低一些
- 在整理存活對象時,因?qū)ο笪恢玫淖儎?,需要該調(diào)整虛擬機棧中的引用地址
- 應(yīng)用場景
- 標(biāo)記-壓縮算法是一種老年代的回收算法,它在標(biāo)記-清除算法的基礎(chǔ)上做了一些優(yōu)化。
(4)垃圾回收算法之分代收集算法文章來源:http://www.zghlxwxcb.cn/news/detail-432173.html
- 針對不同生命周期的對象采用不同的垃圾回收策略,以達到更好的垃圾回收效果
- 年輕代多數(shù)對象存活時間短,高頻進行回收;老年代多數(shù)對象存活時間久,進行低頻回收
- 分代算法是根據(jù)回收對象的特點進行選擇,年輕代適合標(biāo)記-復(fù)制算法,老年代適合標(biāo)記清除或標(biāo)記壓縮算法
- 通過將內(nèi)存劃分為不同的代,可以使得Minor GC的頻率更高,更早地回收垃圾對象,減少Full GC的發(fā)生頻率,提高整體性能
文章來源地址http://www.zghlxwxcb.cn/news/detail-432173.html
- JVM將內(nèi)存分為年輕代和老年代,其中年輕代又分為Eden區(qū)和兩個Survivor區(qū)
- 年輕代
- 用于存放新生的對象,其中Eden區(qū)是新對象的分配區(qū)域
- 當(dāng)Eden區(qū)滿時,會觸發(fā)Minor GC,將存活的對象移動到Survivor區(qū),同時清空Eden區(qū)
- Survivor區(qū)則用于存放經(jīng)過一次Minor GC后存活的對象
- 老年代
- 用于存放長時間存活的對象,當(dāng)年輕代中的對象經(jīng)過多次Minor GC后仍然存活,就會被移動到老年代
- 當(dāng)老年代滿時,會觸發(fā)Full GC
- GC的分類和專業(yè)術(shù)語
- 部分收集(Partial GC)
- 新生代收集
- 對象從Young 區(qū)域消失的過程稱為”minor GC / Young GC “
- Eden 的清理,S0\S1的清理都由于MinorGC
- YoungGen區(qū)內(nèi)存不足,會觸發(fā)minorGC
- 老年代收集
- 對象從老年代中消失的過程,清理整合OldGen的內(nèi)存空間,稱為**”Major GC/Old GC“**
- 有些垃圾收集器 針對老年代單獨回收,所以比較少用
- 新生代收集
- 整堆收集(Full GC)
- 是清理整個堆空間,包括年輕代和老年代
- 理解為Major GC+Minor GC組合后進行的一整個過程,是清理JVM整個堆空間
- FullGC觸發(fā)場景
- 手工調(diào)用System.gc( ), 建議執(zhí)行Full GC,不一定會執(zhí)行,可通過-XX:+ DisableExplicitGC 參數(shù)來禁止調(diào)用System.gc()
- 老年代空間不足 , 通過Minor GC后進入老年代的平均大小大于老年代的可用內(nèi)存
- STW(Stop The World)
- 垃圾回收發(fā)生過程中,用戶線程在運行至安全點(safe point)后,就自行掛起進入暫停狀態(tài),對外的表現(xiàn)就是卡頓
- 所以應(yīng)盡量減少Full GC的次數(shù),是Minor GC還是Major GC都會STW,區(qū)別只在于STW的時間長短
- 部分收集(Partial GC)
到了這里,關(guān)于【Java虛擬機】JVM垃圾回收機制和常見回收算法原理的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!