java虛擬機(jī)常見(jiàn)問(wèn)題
推薦 + 挑錯(cuò) + 收藏(0) + 用戶評(píng)論(0)
一、Java引用的四種狀態(tài):
強(qiáng)引用:
用的最廣。我們平時(shí)寫代碼時(shí),new一個(gè)Object存放在堆內(nèi)存,然后用一個(gè)引用指向它,這就是強(qiáng)引用。
* 如果一個(gè)對(duì)象具有強(qiáng)引用,那垃圾回收器絕不會(huì)回收它*。當(dāng)內(nèi)存空間不足,Java虛擬機(jī)寧愿拋出OutOfMemoryError錯(cuò)誤,使程序異常終止,也不會(huì)靠隨意回收具有強(qiáng)引用的對(duì)象來(lái)解決內(nèi)存不足的問(wèn)題。
軟引用:
如果一個(gè)對(duì)象只具有軟引用,則內(nèi)存空間足夠時(shí),垃圾回收器就不會(huì)回收它;如果內(nèi)存空間不足了,就會(huì)回收這些對(duì)象的內(nèi)存。(備注:如果內(nèi)存不足,隨時(shí)有可能被回收。)
只要垃圾回收器沒(méi)有回收它,該對(duì)象就可以被程序使用。軟引用可用來(lái)實(shí)現(xiàn)內(nèi)存敏感的高速緩存。
弱引用:
弱引用與軟引用的區(qū)別在于:只具有弱引用的對(duì)象擁有更短暫的生命周期。
* 每次執(zhí)行GC的時(shí)候,一旦發(fā)現(xiàn)了只具有弱引用的對(duì)象,不管當(dāng)前內(nèi)存空間足夠與否,都會(huì)回收它的內(nèi)存。不過(guò),由于垃圾回收器是一個(gè)優(yōu)先級(jí)很低的線程,因此不一定會(huì)很快發(fā)現(xiàn)那些只具有弱引用的對(duì)象*。
虛引用:
“虛引用”顧名思義,就是形同虛設(shè),與其他幾種引用都不同,虛引用并不會(huì)決定對(duì)象的生命周期。如果一個(gè)對(duì)象僅持有虛引用,那么它就和沒(méi)有任何引用一樣,在任何時(shí)候都可能被垃圾回收器回收。
虛引用主要用來(lái)跟蹤對(duì)象被垃圾回收器回收的活動(dòng)。
注:關(guān)于各種引用的詳解,可以參考這篇博客:
http://zhangjunhd.blog.51cto.com/113473/53092
二、Java中的內(nèi)存劃分:
Java程序在運(yùn)行時(shí),需要在內(nèi)存中的分配空間。為了提高運(yùn)算效率,就對(duì)數(shù)據(jù)進(jìn)行了不同空間的劃分,因?yàn)槊恳黄瑓^(qū)域都有特定的處理數(shù)據(jù)方式和內(nèi)存管理方式。
上面這張圖就是jvm運(yùn)行時(shí)的狀態(tài)。具體劃分為如下5個(gè)內(nèi)存空間:(非常重要)
- 程序計(jì)數(shù)器:保證線程切換后能恢復(fù)到原來(lái)的執(zhí)行位置
- 虛擬機(jī)棧:(棧內(nèi)存)為虛擬機(jī)執(zhí)行java方法服務(wù):方法被調(diào)用時(shí)創(chuàng)建棧幀–》局部變量表-》局部變量、對(duì)象引用
- 本地方法棧:為虛擬機(jī)執(zhí)使用到的Native方法服務(wù)
- 堆內(nèi)存:存放所有new出來(lái)的東西
- 方法區(qū):存儲(chǔ)被虛擬機(jī)加載的類信息、常量、靜態(tài)常量、靜態(tài)方法等。
- 運(yùn)行時(shí)常量池(方法區(qū)的一部分)
GC對(duì)它們的回收:
內(nèi)存區(qū)域中的程序計(jì)數(shù)器、虛擬機(jī)棧、本地方法棧這3個(gè)區(qū)域隨著線程而生,線程而滅;棧中的棧幀隨著方法的進(jìn)入和退出而有條不紊地執(zhí)行著出棧和入棧的操作,每個(gè)棧幀中分配多少內(nèi)存基本是在類結(jié)構(gòu)確定下來(lái)時(shí)就已知的。在這幾個(gè)區(qū)域不需要過(guò)多考慮回收的問(wèn)題,因?yàn)榉椒ńY(jié)束或者線程結(jié)束時(shí),內(nèi)存自然就跟著回收了。
GC回收的主要對(duì)象:而J**ava堆和方法區(qū)**則不同,一個(gè)接口中的多個(gè)實(shí)現(xiàn)類需要的內(nèi)存可能不同,一個(gè)方法中的多個(gè)分支需要的內(nèi)存也可能不一樣,我們只有在程序處于運(yùn)行期間時(shí)才能知道會(huì)創(chuàng)建哪些對(duì)象,這部分內(nèi)存的分配和回收都是動(dòng)態(tài)的,GC關(guān)注的也是這部分內(nèi)存,后面的文章中如果涉及到“內(nèi)存”分配與回收也僅指著一部分內(nèi)存。
1、程序計(jì)數(shù)器:(線程私有)
每個(gè)線程擁有一個(gè)程序計(jì)數(shù)器,在線程創(chuàng)建時(shí)創(chuàng)建,
指向下一條指令的地址
執(zhí)行本地方法時(shí),其值為undefined
說(shuō)的通俗一點(diǎn),我們知道,Java是支持多線程的,程序先去執(zhí)行A線程,執(zhí)行到一半,然后就去執(zhí)行B線程,然后又跑回來(lái)接著執(zhí)行A線程,那程序是怎么記住A線程已經(jīng)執(zhí)行到哪里了呢?這就需要程序計(jì)數(shù)器了。因此,為了線程切換后能夠恢復(fù)到正確的執(zhí)行位置,每條線程都有一個(gè)獨(dú)立的程序計(jì)數(shù)器,這塊兒屬于“線程私有”的內(nèi)存。
2、Java虛擬機(jī)棧:(線程私有)
每個(gè)方法被調(diào)用的時(shí)候都會(huì)創(chuàng)建一個(gè)棧幀,用于存儲(chǔ)局部變量表、操作棧、動(dòng)態(tài)鏈接、方法出口等信息。局部變量表存放的是:編譯期可知的基本數(shù)據(jù)類型、對(duì)象引用類型。
每個(gè)方法被調(diào)用直到執(zhí)行完成的過(guò)程,就對(duì)應(yīng)著一個(gè)棧幀在虛擬機(jī)中從入棧到出棧的過(guò)程。
在Java虛擬機(jī)規(guī)范中,對(duì)這個(gè)區(qū)域規(guī)定了兩種異常情況:
(1)如果線程請(qǐng)求的棧深度太深,超出了虛擬機(jī)所允許的深度,就會(huì)出現(xiàn)StackOverFlowError(比如無(wú)限遞歸。因?yàn)槊恳粚訔颊加靡欢臻g,而 Xss 規(guī)定了棧的最大空間,超出這個(gè)值就會(huì)報(bào)錯(cuò))
(2)虛擬機(jī)棧可以動(dòng)態(tài)擴(kuò)展,如果擴(kuò)展到無(wú)法申請(qǐng)足夠的內(nèi)存空間,會(huì)出現(xiàn)OOM
3、本地方法棧:
(1)本地方法棧與java虛擬機(jī)棧作用非常類似,其區(qū)別是:java虛擬機(jī)棧是為虛擬機(jī)執(zhí)行java方法服務(wù)的,而本地方法棧則為虛擬機(jī)執(zhí)使用到的Native方法服務(wù)。
(2)Java虛擬機(jī)沒(méi)有對(duì)本地方法棧的使用和數(shù)據(jù)結(jié)構(gòu)做強(qiáng)制規(guī)定,Sun HotSpot虛擬機(jī)就把java虛擬機(jī)棧和本地方法棧合二為一。
(3)本地方法棧也會(huì)拋出StackOverFlowError和OutOfMemoryError。
4、Java堆:即堆內(nèi)存(線程共享)
(1)堆是java虛擬機(jī)所管理的內(nèi)存區(qū)域中最大的一塊,java堆是被所有線程共享的內(nèi)存區(qū)域,在java虛擬機(jī)啟動(dòng)時(shí)創(chuàng)建,堆內(nèi)存的唯一目的就是存放對(duì)象實(shí)例幾乎所有的對(duì)象實(shí)例都在堆內(nèi)存分配。
(2)堆是GC管理的主要區(qū)域,從垃圾回收的角度看,由于現(xiàn)在的垃圾收集器都是采用的分代收集算法,因此java堆還可以初步細(xì)分為新生代和老年代。
(3)Java虛擬機(jī)規(guī)定,堆可以處于物理上不連續(xù)的內(nèi)存空間中,只要邏輯上連續(xù)的即可。在實(shí)現(xiàn)上既可以是固定的,也可以是可動(dòng)態(tài)擴(kuò)展的。如果在堆內(nèi)存沒(méi)有完成實(shí)例分配,并且堆大小也無(wú)法擴(kuò)展,就會(huì)拋出OutOfMemoryError異常。
5、方法區(qū):(線程共享)
(1)用于存儲(chǔ)已被虛擬機(jī)加載的類信息、常量、靜態(tài)變量、即時(shí)編譯器編譯后的代碼等數(shù)據(jù)。
(2)Sun HotSpot虛擬機(jī)把方法區(qū)叫做永久代(Permanent Generation),方法區(qū)中最終要的部分是運(yùn)行時(shí)常量池。
6、運(yùn)行時(shí)常量池:
(1)運(yùn)行時(shí)常量池是方法區(qū)的一部分,自然受到方法區(qū)內(nèi)存的限制,當(dāng)常量池?zé)o法再申請(qǐng)到內(nèi)存時(shí)就會(huì)拋出OutOfMemoryError異常。
三、Java對(duì)象在內(nèi)存中的狀態(tài):
可達(dá)的/可觸及的:
Java對(duì)象被創(chuàng)建后,如果被一個(gè)或多個(gè)變量引用,那就是可達(dá)的。即從根節(jié)點(diǎn)可以觸及到這個(gè)對(duì)象。
其實(shí)就是從根節(jié)點(diǎn)掃描,只要這個(gè)對(duì)象在引用鏈中,那就是可觸及的。
可恢復(fù)的:
Java對(duì)象不再被任何變量引用就進(jìn)入了可恢復(fù)狀態(tài)。
在回收該對(duì)象之前,該對(duì)象的finalize()方法進(jìn)行資源清理。如果在finalize()方法中重新讓變量引用該對(duì)象,則該對(duì)象再次變?yōu)榭蛇_(dá)狀態(tài),否則該對(duì)象進(jìn)入不可達(dá)狀態(tài)
不可達(dá)的:
Java對(duì)象不被任何變量引用,且系統(tǒng)在調(diào)用對(duì)象的finalize()方法后依然沒(méi)有使該對(duì)象變成可達(dá)狀態(tài)(該對(duì)象依然沒(méi)有被變量引用),那么該對(duì)象將變成不可達(dá)狀態(tài)。
當(dāng)Java對(duì)象處于不可達(dá)狀態(tài)時(shí),系統(tǒng)才會(huì)真正回收該對(duì)象所占有的資源。
四、判斷對(duì)象死亡的兩種常用算法:
當(dāng)對(duì)象不被引用的時(shí)候,這個(gè)對(duì)象就是死亡的,等待GC進(jìn)行回收。
1、引用計(jì)數(shù)算法:
概念:
給對(duì)象中添加一個(gè)引用計(jì)數(shù)器,每當(dāng)有一個(gè)地方引用它時(shí),計(jì)數(shù)器值就加1;當(dāng)引用失效時(shí),計(jì)數(shù)器值就減1;任何時(shí)刻計(jì)數(shù)器為0的對(duì)象就是不可能再被使用的。
但是:
主流的java虛擬機(jī)并沒(méi)有選用引用計(jì)數(shù)算法來(lái)管理內(nèi)存,其中最主要的原因是:它很難解決對(duì)象之間相互循環(huán)引用的問(wèn)題。
優(yōu)點(diǎn):
算法的實(shí)現(xiàn)簡(jiǎn)單,判定效率也高,大部分情況下是一個(gè)不錯(cuò)的算法。很多地方應(yīng)用到它
缺點(diǎn):
引用和去引用伴隨加法和減法,影響性能
致命的缺陷:對(duì)于循環(huán)引用的對(duì)象無(wú)法進(jìn)行回收
2、根搜索算法:(jvm采用的算法)
概念:
設(shè)立若干種根對(duì)象,當(dāng)任何一個(gè)根對(duì)象(GC Root)到某一個(gè)對(duì)象均不可達(dá)時(shí),則認(rèn)為這個(gè)對(duì)象是可以被回收的。
注:這里提到,設(shè)立若干種根對(duì)象,當(dāng)任何一個(gè)根對(duì)象到某一個(gè)對(duì)象均不可達(dá)時(shí),則認(rèn)為這個(gè)對(duì)象是可以被回收的。我們?cè)诤竺娼榻B標(biāo)記-清理算法/標(biāo)記整理算法時(shí),也會(huì)一直強(qiáng)調(diào)從根節(jié)點(diǎn)開始,對(duì)所有可達(dá)對(duì)象做一次標(biāo)記,那什么叫做可達(dá)呢?
可達(dá)性分析:
從根(GC Roots)的對(duì)象作為起始點(diǎn),開始向下搜索,搜索所走過(guò)的路徑稱為“引用鏈”,當(dāng)一個(gè)對(duì)象到GC Roots沒(méi)有任何引用鏈相連(用圖論的概念來(lái)講,就是從GC Roots到這個(gè)對(duì)象不可達(dá))時(shí),則證明此對(duì)象是不可用的。
如上圖所示,ObjectD和ObjectE是互相關(guān)聯(lián)的,但是由于GC roots到這兩個(gè)對(duì)象不可達(dá),所以最終D和E還是會(huì)被當(dāng)做GC的對(duì)象,上圖若是采用引用計(jì)數(shù)法,則A-E五個(gè)對(duì)象都不會(huì)被回收。
根(GC Roots):
說(shuō)到GC roots(GC根),在JAVA語(yǔ)言中,可以當(dāng)做GC roots的對(duì)象有以下幾種:
1、棧(棧幀中的本地變量表)中引用的對(duì)象。
2、方法區(qū)中的靜態(tài)成員。
3、方法區(qū)中的常量引用的對(duì)象(全局變量)
4、本地方法棧中JNI(一般說(shuō)的Native方法)引用的對(duì)象。
注:第一和第四種都是指的方法的本地變量表,第二種表達(dá)的意思比較清晰,第三種主要指的是聲明為final的常量值。
在根搜索算法的基礎(chǔ)上,現(xiàn)代虛擬機(jī)的實(shí)現(xiàn)當(dāng)中,垃圾搜集的算法主要有三種,分別是標(biāo)記-清除算法、復(fù)制算法、標(biāo)記-整理算法。這三種算法都擴(kuò)充了根搜索算法,不過(guò)它們理解起來(lái)還是非常好理解的。
非常好我支持^.^
(0) 0%
不好我反對(duì)
(0) 0%