引用有哪些類型架专?
強引用:通過new創(chuàng)建出來的對象。只要強引用存在玄帕,垃圾回收器將不會回收部脚。
軟引用:通過SoftReference實現(xiàn)軟引用,系統(tǒng)將要發(fā)生內(nèi)存溢出之前才會對這些對象進行回收裤纹。
弱引用:通過WeakReference實現(xiàn)弱引用委刘,無論當內(nèi)存足夠丧没,GC運行時都會進行回收。
虛引用:通過PhantomReference實現(xiàn)锡移,通過虛引用無法回去對象的實例呕童,虛引用的作用就是當此對象被回收時,會收到一個系統(tǒng)通知淆珊。
如何判斷一個對象是否應該被回收夺饲?
1、引用計數(shù)算法施符,每當有一個地方引用他往声,就加1,引用失效戳吝,就減1.數(shù)量為0浩销,說明可以進行回收。
引用計數(shù)算法的缺點就是無法判斷兩個對象之間存在相互引用的情況听哭。
jvm中沒有使用這種判斷方式
2慢洋、可達性分析算法,通過一系列GCRoot對象作為起始點陆盘,從這些點開始向下搜索普筹,搜索的路徑成為引用鏈,當一個對象到GC沒有任何引用鏈礁遣,說明對象可以被回收斑芜。
GCRoot對象有哪些肩刃?
(1)虛擬機棧的棧幀中引用的對象祟霍。
(2)方法區(qū)中靜態(tài)屬性引用的對象。
(3)方法區(qū)中常亮引用的對象盈包。
(4)本地方法棧中jni引用的對象沸呐。
枚舉根節(jié)點時候需要GC停頓,保證分析結(jié)果的準確性呢燥。
使用GCRoot枚舉根節(jié)點崭添,由于在整個方法區(qū)進行枚舉會耗費時間。如何解決叛氨?
執(zhí)行準確式GC并不需要檢查執(zhí)行上下文中所有的引用的位置呼渣,在Hotspor中通過OopMap的數(shù)據(jù)結(jié)構(gòu)來達到這個目的。在類加載完成的時候寞埠,虛擬機會將對象內(nèi)什么偏移量什么數(shù)據(jù)計算出來屁置,在JIT編譯的時候,會在特定的位置記住棧和寄存器中什么地方存在引用仁连,GC直接對這些引用進行掃描蓝角。
什么是安全點?
OopMap雖然可以進行快速準確的進行GC Root枚舉,但是由于虛擬機的指令太多使鹅,如果為每個指令都生成對應的OopMap會浪費大量的空間揪阶,所以虛擬機會在特定的位置生成OopMap,這些特定的位置稱作安全點患朱。所以程序不是在任何時候都能夠進行GC,只有到達安全點才能進行GC裁厅。
安全點如何選定倦淀?
依據(jù)是否能夠讓程序長時間的運行為特點進行選定,由于每條指令的運行時間都十分短,所以一般選用的點為方法的調(diào)用,循環(huán)跳轉(zhuǎn)甘邀,異常跳轉(zhuǎn)等。
發(fā)生GC時需要線程停頓,如何讓線程在發(fā)生GC時候跑到最近的安全點停頓下來?
1褂傀、搶先式中斷:發(fā)生GC時中斷所有的線程,如果發(fā)現(xiàn)某條線程不在安全點,就恢復此線程讓他跑到安全點上。(虛擬機一般不使用)
2、主動式中斷:不對線程進行操作。設置一個標志位,讓所有的線程去輪詢標志位,發(fā)現(xiàn)標志位為真,就自動掛起。輪詢標志的地方和安全點是重合的鸠儿,再加上創(chuàng)建對象需要分配內(nèi)存的地方。
什么是安全區(qū)域命斧?
安全點解決了GC問題田晚,但是當發(fā)生GC的時候線程處于sleep狀態(tài),此時線程無法響應中斷請求国葬。此時需要使用安全區(qū)域進行解決贤徒。安全區(qū)域就是代碼片段中引用關(guān)系不會發(fā)生變化的地方芹壕。當線程執(zhí)行到安全區(qū)域的時候,會對線程進行標記接奈,發(fā)生GC時候踢涌,jvm不管這些線程,在GC的時候序宦,如果這些線程要離開安全區(qū)域睁壁,此時,判斷jvm是否已經(jīng)完成GC互捌,如果完成潘明,則線程執(zhí)行,如果沒有完成秕噪,則線程停頓等待GC完成的信號钳降。(當線程發(fā)生sleep時正處于安全區(qū)域)
可達性算法不可達的對象就一定會被回收嗎?
不一定腌巾,當發(fā)現(xiàn)對象不可達的時候牲阁,將會對此對象進行第一次標記,對標記的對象就行篩選壤躲,篩選的條件是是否有必要執(zhí)行finalize()方法城菊。
當此對象已經(jīng)調(diào)用過finalize()方法或者在對象中沒有覆蓋finalize()方法,則判定次對象沒有必要執(zhí)行finalize()方法碉克。
沒有必要執(zhí)行finalize()方法的對象將會直接被回收凌唬。
有必要執(zhí)行finalize()方法的對象放在一個隊列中,之后有虛擬機創(chuàng)建一個低優(yōu)先級的線程去出發(fā)隊列中的對象的finalize()方法漏麦,注意此處為觸發(fā)并非等待finalize()執(zhí)行結(jié)束客税,防止finalize()方法中出現(xiàn)死循環(huán)導致回收系統(tǒng)崩潰。
當一個對象的finalize()方法執(zhí)行結(jié)束后撕贞,方法并沒有被回收更耻,稍后會對隊列中的對象進行二次標記,此時標記的依據(jù)是對象是否可達捏膨。如果還是不可達秧均,才會將此對象放入即將回收的集合。所以finalize()方法中如果為對象添加引用鏈号涯,可以拯救此對象目胡。
注意:每個對象的finalize()方法只會被jvm調(diào)用一次,如果一個對象在第一次執(zhí)行finalize()時候被拯救链快,在下次執(zhí)行回收會直接對對象就行回收誉己,將不會調(diào)用對象的finalize()方法。
方法區(qū)中沒有垃圾回收域蜗?
方法區(qū)有垃圾回收巨双,但是回收的效率低噪猾。
方法區(qū)只要回收廢棄的常量和無用的類。
如果沒有任何地方對此常量進行引用筑累,則此常量就會被回收畏妖。
方法區(qū)中哪些類是無用的類?
(1)java堆中不存在該類的任何實例疼阔。
(2)加載該類的ClassLoader已經(jīng)被回收戒劫。
(3)該類的class對象沒有任何地方被引用。
滿足以上三個條件的類可以被回收婆廊,而不是和java堆中的對象一樣必然會被回收迅细。
對象的創(chuàng)建是如何創(chuàng)建的(new)?:
當虛擬機遇到new指令的時候淘邻,先去檢查這個指令的參數(shù)是否能夠在常量池中定位到一個符號的引用(類的全限定名)茵典,在判斷這個符號引用代表的類是否已經(jīng)被加載,如果沒有被加載宾舅,將會進行類的加載操作统阿。通過加載檢查后,將對類分配內(nèi)存筹我。
對象創(chuàng)建時內(nèi)存分配方式扶平?
1、指針碰撞蔬蕊,對象需要多大的內(nèi)存在類加載完成的時候就已經(jīng)確定结澄,此內(nèi)存分配方式相當于,將已分配的內(nèi)存放在放在一邊岸夯,為分配的內(nèi)存放在另一邊麻献,中間用指針進行隔離,當需要分配內(nèi)存的時候只需要移動指針即可猜扮。
2勉吻、空閑列表,即維護一個列表旅赢,記錄內(nèi)存中還沒有分配的內(nèi)存位置齿桃。
分配內(nèi)存的過程并非是線程安全的解決方案。
1鲜漩、失敗重試源譬。
2、為每個線程分配本地線程緩沖區(qū)(TLAB)孕似,當TLAB使用完之后分配新的TLAB的時候,實行同步鎖定刮刑。分配結(jié)束后喉祭,將分配的內(nèi)存除對象頭外养渴,其余初始化為0值。如果使用TLAB泛烙,則初始化為0值提前到TLAB分配時執(zhí)行理卑。
GC對內(nèi)存的分配。
內(nèi)存一般分為新生代和老年代蔽氨,新生代又分為一塊較大的Eden和兩塊較小的Survivor區(qū)域(HotSpot虛擬機E:S=8:1)藐唠,
新生的對象優(yōu)先分配在新生代的E區(qū),如果啟用本地線程緩沖鹉究,優(yōu)先在TLAB上進行分配宇立,少數(shù)情況也會直接在老年代進行對象的分配,當在E分配對象發(fā)現(xiàn)內(nèi)存不夠使用的時候自赔,會發(fā)生新生代的GC將E區(qū)對象轉(zhuǎn)入S區(qū)妈嘹,當發(fā)現(xiàn)S區(qū)無法存放時,通過分配擔保將對象轉(zhuǎn)入老年代绍妨。
對象進入老年代的判定润脸。
大對象直接進入老年代(閥值可以通過參數(shù)進行設定),為了避免E區(qū)和S區(qū)之間發(fā)生大量的內(nèi)存復制他去。
長期存活的對象進入老年代毙驯,虛擬機給每個對象定義了一個年齡計數(shù)器,在E區(qū)中的對象經(jīng)過一次GC仍然存活并能夠被S區(qū)容納灾测,設置此對象的年齡為1尔苦,在S區(qū)中的對象,每熬過一次GC行施,就將年齡加1允坚,當年齡達到一定的程度(默認是15,可以通過參數(shù)進行設置)就會進入老年代蛾号,
動態(tài)年齡判斷稠项,不一定只有年齡達到閥值才會進入老年代,當相同年齡的對象的總大小大于S空間的一半鲜结,則大于等于這個年齡的對象將會進入老年代展运。
什么是空間分配擔保?
當新生代發(fā)生GC的時候精刷,先判斷老年代中的剩余空間的總大小是否大于新生代中的總對象的大小拗胜,如果是,則發(fā)生進行新生代的GC怒允,如果不是埂软,則判斷是否允許擔保失敗,如果不允許纫事,則進行老年代的GC,如果允許勘畔,則判斷老年代中的剩余空間的總大小是否大于新生代每次發(fā)生GC時進入老年代的對象的平均值所灸,如果是,則進行新生代的GC炫七,如果不是爬立,則進行一次老年代的GC。
垃圾回收算法有那些万哪?
1侠驯、標記-清除:標記所有需要被回收的對象,然后回收奕巍。次算法效率低吟策,并且產(chǎn)生內(nèi)存碎片,由于老年代中存活的對象多伍绳,在老年代中進行使用踊挠。
2、復制算法:將內(nèi)存劃分為等大小的兩塊冲杀,每次使用其中的一塊效床,回收時,將存活的對象復制到?jīng)]有使用的一塊內(nèi)存中权谁,然后對使用的內(nèi)存一次性就行清理剩檀。實現(xiàn)簡單,運行高效旺芽,但是存在大量內(nèi)存的浪費沪猴。由于新生代中存活的對象少,新生代中使用這種算法將E區(qū)存活的對象復制到S區(qū)采章。
3运嗜、標記整理算法:讓所有存活的對象往一側(cè)移動,然后清楚另一側(cè)悯舟。在老年代中使用這種算法担租,避免產(chǎn)生內(nèi)存碎片。
文中有些和GC無關(guān)的知識抵怎,如對象的創(chuàng)建奋救,引用的類型等,知識為了讓讀者更好的了解GC反惕,如果有疑問可以留言隨時討論尝艘。