GC(垃圾收集),那收集回收的是什么呢?是內(nèi)存,所以在了解垃圾回收機(jī)制之前,要對Java內(nèi)存有一個(gè)了解。
一,Java內(nèi)存
圖解:

私有內(nèi)存區(qū)——伴隨線程的產(chǎn)生而產(chǎn)生,一旦線程終止,私有內(nèi)存區(qū)也會自動消除
程序計(jì)數(shù)器:指示當(dāng)前程序執(zhí)行到了哪一行,執(zhí)行Java方法時(shí)記錄正在執(zhí)行的虛擬機(jī)字節(jié)碼指令地址;執(zhí)行本地方法時(shí),計(jì)數(shù)器值為null
虛擬機(jī)棧:用于執(zhí)行Java方法,棧針存儲布局邊聊表,操作數(shù)棧,動態(tài)鏈接,方法返回地址和一些額外的符加信息。程序執(zhí)行時(shí)入棧;執(zhí)行完成后棧針出棧。
GC主要就是在Java堆中進(jìn)行的。
Java堆:Java虛擬機(jī)管理的內(nèi)存中最大的一塊,所有線程共享,幾乎所有的對象實(shí)例和數(shù)組都在這類分配內(nèi)存。
堆內(nèi)存又分為:新生代和老年代,并且一般新時(shí)代的空間比老年代大。
了解了Java內(nèi)存,接下來就來了解一下GC原理:
二.垃圾回收機(jī)制
一)GC的主要任務(wù):
1.分配內(nèi)存;
2.確保被引用對象的內(nèi)存不被錯(cuò)誤的回收;
3.回收不再被引用的對象的內(nèi)存空間
二)垃圾回收機(jī)制的主要解決問題
1.哪些內(nèi)存需要回收?
2.什么時(shí)候回收?
3.如何回收?
針對問題一,垃圾收集器會對堆進(jìn)行回收前,確定對象中哪些是“存活”,哪些是”死亡“(不可能再被任何途徑使用的對象)
判斷方法:
1.引用計(jì)數(shù)算法
每當(dāng)一個(gè)地方引用它時(shí),計(jì)數(shù)器+1;引用失效時(shí),計(jì)數(shù)器-1;計(jì)數(shù)值=0——不可能再被引用。
//舉例:
? ? ? ? Test test1 = new Test();
? ? ? ? Test test2 = new Test();
? ? ? ? test1.obj = test2;
? ? ? ? test2.obj = test1;
? ? ? ? //test1 ,test12能否被回收?
? ? ? ? System.gc();
1
2
3
4
5
6
7
查看運(yùn)行結(jié)果,會發(fā)現(xiàn)并沒有因?yàn)閮蓚€(gè)對象互相引用就沒有回收,因此引用計(jì)數(shù)算法很難解決對象之間相互矛盾循環(huán)引用的問題。
2.可達(dá)性分析算法:
向圖,樹圖,把一系列“GC Roots”作為起始點(diǎn),從節(jié)點(diǎn)向下搜索,路徑稱為引用鏈,當(dāng)一個(gè)對象到GC Roots沒有任何引用鏈相連,即不可達(dá)時(shí),則證明此對象時(shí)不可用的。
舉例:一顆樹有很多丫枝,其中一個(gè)分支斷了,跟樹上沒有任何聯(lián)系,那就說明這個(gè)分支沒有用了,就可以當(dāng)垃圾回收去燒了。
注:在Java中可作為GCRoots的對象:
1).虛擬機(jī)棧(棧幀中的本地變量表)中引用的對象;
2).方法區(qū)中類靜態(tài)屬性引用的對象;
3).方法區(qū)中常量引用的對象;
4).本地方法棧中JNI引用的對象
針對問題2——什么時(shí)候回收?
即使是被判斷不可達(dá)的對象,也要再進(jìn)行篩選,當(dāng)對象沒有覆蓋finalize()方法,或者finalize方法已經(jīng)被虛擬機(jī)調(diào)用過,則沒有必要執(zhí)行;
如果有必要執(zhí)行——放置在F-Queue的隊(duì)列中——Finalizer線程執(zhí)行。
注意:對象可以在被GC時(shí)可以自我拯救(this),機(jī)會只有一次,因?yàn)槿魏我粋€(gè)對象的finalize()方法都只會被系統(tǒng)自動調(diào)用一次。并不建議使用,應(yīng)該避免。使用try_finaly或者其他方式。
問題3——如何回收,這就牽扯到垃圾收集算法和垃圾收集器
垃圾收集算法:
1.標(biāo)記—清除算法
兩個(gè)階段:標(biāo)記,清除;
不足:效率問題;空間問題(會產(chǎn)生大量不連續(xù)的內(nèi)存碎片)
2.復(fù)制算法
將可用內(nèi)存按容量分為大小相等的兩塊,每次都只使用其中一塊;
不足:將內(nèi)存縮小為了原來的一半
新生代
3.標(biāo)記—整理算法
標(biāo)記,清除(讓存活的對象都向一端移動)
老年代
垃圾收集器:

最后來講一下流程。
新建的對象再新生代中,如果新生代內(nèi)存不夠,就進(jìn)行Minor GC釋放掉不活躍對象;如果還是不夠,就把部分活躍對象復(fù)制到老年代中,如果還是不夠,就進(jìn)行MajorGC釋放老年代,如果還是不夠,JVM會拋出內(nèi)存不足,發(fā)生oom,內(nèi)存泄漏。
額外參考資料:
https://blog.csdn.net/weixin_41835916/article/details/81530733
JVM性能調(diào)優(yōu)建議:
jvm調(diào)優(yōu)沒有一個(gè)固定模板配置說必須如何操作,它需要根據(jù)系統(tǒng)的情況不同對待。
但是可以有如下建議:
1、初始化內(nèi)存和最大內(nèi)存盡量保持一致,避免內(nèi)存不夠用繼續(xù)擴(kuò)充內(nèi)存。最大內(nèi)存不要超過物理內(nèi)存,例如內(nèi)存8g,你可以設(shè)置最大內(nèi)存4g/6g但是不能超過8g否則加載類的時(shí)候沒有空間會報(bào)錯(cuò)。
2、gc/full gc頻率不要太高、每次gc時(shí)間不要太長、根據(jù)系統(tǒng)應(yīng)用來定。