面試題:JVM,GC垃圾回收機(jī)制

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)用來定。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 這篇文章是我之前翻閱了不少的書籍以及從網(wǎng)絡(luò)上收集的一些資料的整理,因此不免有一些不準(zhǔn)確的地方,同時(shí)不同JDK版本的...
    高廣超閱讀 16,057評論 3 83
  • 作者:一字馬胡 轉(zhuǎn)載標(biāo)志 【2017-11-12】 更新日志 日期更新內(nèi)容備注 2017-11-12新建文章初版 ...
    beneke閱讀 2,330評論 0 7
  • 序言 相信各位Android和Java開發(fā)的同學(xué)都知道,Java與C++不同,JVM會自動管理內(nèi)存,即自動幫我們分...
    左大人閱讀 3,025評論 0 14
  • 介紹JVM中7個(gè)區(qū)域,然后把每個(gè)區(qū)域可能造成內(nèi)存的溢出的情況說明 程序計(jì)數(shù)器:看做當(dāng)前線程所執(zhí)行的字節(jié)碼行號指示器...
    jemmm閱讀 2,306評論 0 9
  • JVM架構(gòu) 當(dāng)一個(gè)程序啟動之前,它的class會被類裝載器裝入方法區(qū)(Permanent區(qū)),執(zhí)行引擎讀取方法區(qū)的...
    cocohaifang閱讀 1,845評論 0 7

友情鏈接更多精彩內(nèi)容