哪些區(qū)域需要回收
運(yùn)行時(shí)內(nèi)存分為5個(gè)區(qū)域:程序計(jì)數(shù)器、虛擬機(jī)棧哼转、本地方法棧猫态、堆义辕、方法區(qū)基显,這些區(qū)域是如何回收的销部?
1-程序計(jì)數(shù)器雨膨、虛擬機(jī)棧、本地方法棧
這3個(gè)區(qū)域是隨著線程而生,隨著線程而滅,棧中的棧幀數(shù)據(jù)隨著方法的調(diào)用到結(jié)束有條不紊的進(jìn)行著入棧出棧,每一個(gè)棧幀中分配多少內(nèi)部基本上在類結(jié)構(gòu)確定下來(類加載)就已知了蔬充。內(nèi)存分配和回收具有確定性愕提,隨著方法的結(jié)束或者線程的結(jié)束后,內(nèi)存自然就會(huì)被回收了如输。
2- java堆和方法區(qū)
一個(gè)接口中的多個(gè)實(shí)現(xiàn)類需要的內(nèi)存可能不一樣缎谷,一個(gè)方法的多個(gè)分支需要的內(nèi)存也不一樣,我們只有在程序處于執(zhí)行期間才能知道會(huì)創(chuàng)建哪些對(duì)象春感,對(duì)這部分的內(nèi)存分配和回收是動(dòng)態(tài)的,垃圾回收主要關(guān)注的是這部分的內(nèi)存竟纳。
判斷對(duì)象是“生”是“死”?
引用計(jì)數(shù)算法
java虛擬機(jī)沒有采用引用計(jì)數(shù)算法來判斷對(duì)象是否生存颅筋,主要考慮的是它很難解決對(duì)象之間的相互循環(huán)引用的問題。
可達(dá)性分析算法(Reachablity Analysis)
基本思路是:從每一個(gè)GC Roots出發(fā)找到它所有可達(dá)的對(duì)象用押,走過的路徑成為引用鏈蜻拨,被引用鏈串起來的就是存活對(duì)象厘灼;其他的雖然存在引用關(guān)系闪朱,但是GC Roots不可達(dá)的,判定為可回收對(duì)象。澜术、
GC Roots :可作為的對(duì)象包含以下幾種:
- 虛擬機(jī)棧(棧幀中本地變量表)中引用的對(duì)象
- 方法區(qū)中類靜態(tài)屬性引用的對(duì)象
- 方法區(qū)中常量引用的對(duì)象
- 本地方法棧中JNI(Native Method)引用的對(duì)象
引用與緩存
為了更細(xì)致的管理對(duì)象的內(nèi)存艺蝴,對(duì)象引用進(jìn)行了擴(kuò)展,分為:強(qiáng)引用(Strong Reference)鸟废、軟引用(Soft Reference)猜敢、弱引用(Weak Reference)、虛引用(Phantom Reference盒延,也叫幽靈引用)缩擂。
強(qiáng)引用
-
Object obj = new Object();
這種通過關(guān)鍵字new出來的對(duì)象就屬于強(qiáng)引用,普遍存在的添寺,只要這種引用還關(guān)聯(lián)著對(duì)象胯盯,就不會(huì)被垃圾回收器回收。 - **內(nèi)存空間不夠時(shí)计露,不會(huì)進(jìn)行垃圾回收博脑,而是直接拋出內(nèi)存溢出異常 **
軟引用
用來描述有用但是并不必需的對(duì)象,在系統(tǒng)將要發(fā)生內(nèi)存溢出異常之前票罐,JVM將會(huì)把這些對(duì)象標(biāo)記成垃圾數(shù)據(jù)叉趣,并對(duì)這些標(biāo)記的數(shù)據(jù)進(jìn)行二次回收,若回收后還是沒有足夠的內(nèi)存分配该押,才會(huì)拋出內(nèi)存溢出的異常疗杉。 用法: 用來實(shí)現(xiàn)內(nèi)存敏感的緩存
import java.lang.ref.SoftReference;
class ReferenceTest{
Object obj = new Object();//強(qiáng)引用
SoftReference<Object> sr = new SoftReference<Object>(obj);//關(guān)聯(lián)軟引用
// 取消強(qiáng)引用,只保留軟引用
obj=null沈善;
pass(...);
//通用的處理乡数,以及softreference的復(fù)用模式
if(sr.get() != null){
Obeject getRef = sr.get();
use(getRef);
}else{
Object newObj = new Object();
sr = new SoftReference(newObj);//重建軟引用
}
}
- 注意
1.在虛擬機(jī)內(nèi)存空間足夠大的時(shí)候是不會(huì)發(fā)生內(nèi)存回收的,而且GC的優(yōu)先級(jí)最低闻牡,只有所有線程都停止時(shí)才有可能執(zhí)行GC(即使使用System.gc()也只是告訴JVM這是一個(gè)執(zhí)行GC的好時(shí)機(jī)净赴,但是實(shí)際上JVM會(huì)自己決斷是否達(dá)到了這個(gè)條件,比如沒有線程正在執(zhí)行罩润,這是GC這個(gè)守護(hù)線程就會(huì)執(zhí)行)玖翅,因?yàn)镚C有一定的代價(jià)。所以與軟引用關(guān)聯(lián)的對(duì)象在內(nèi)存充足時(shí)是不會(huì)發(fā)生的割以,只有處于內(nèi)存溢出的邊緣才會(huì)發(fā)生回收操作金度。
2.雖然與軟引用關(guān)聯(lián)的對(duì)象可能會(huì)被收回,但保存這個(gè)軟引用關(guān)系的SoftReference變量并不一定會(huì)被回收严沥,所以出現(xiàn)了ReferenceQueue來管理這些變量猜极。
弱引用
- 弱引用描述的是非必需的對(duì)象:被弱引用關(guān)聯(lián)的對(duì)象只能生存到下一次GC回收發(fā)生之前,當(dāng)GC工作時(shí)消玄,無論當(dāng)前空間是否足夠跟伏,都會(huì)回收只被弱引用關(guān)聯(lián)的對(duì)象丢胚。
- 用法:一般會(huì)和ReferenceQueue進(jìn)行關(guān)聯(lián)
import java.lang.ref.ReferenceQueue;
import java.lang.ref.WeakReference;
public class WeakReferenceTest{
public static void main(Stting[] args) throws Exception{
Object obj = new Object();
//ReferenceQueue的作用:
//當(dāng)弱引用關(guān)聯(lián)的對(duì)象被標(biāo)記為垃圾,準(zhǔn)備回收時(shí)受扳,
//會(huì)自動(dòng)把保存弱引用的對(duì)象WeakReference放到ReferenceQueue中携龟,等待被處理
//主要目的是:引用關(guān)聯(lián)的對(duì)象被回收了,但存儲(chǔ)引用的對(duì)象沒有及時(shí)回收會(huì)造成
//內(nèi)存膨脹
ReferenceQueue<Object> rf = new ReferenceQueue<Object>();
WeakReference<Object> wf = new WeakReference<Object>(obj,rf);
WeakReference<Object> wf_q = null;
// poll方法勘高,刪除隊(duì)尾引用并返回
while(wf_q = rf.poll()!= null){
// do something for remove reference
}
}
}
虛引用
- 最弱的一種引用關(guān)系峡蟋,無法通過虛引用來獲取對(duì)象的實(shí)例
- 用途:僅僅是在這個(gè)對(duì)象被回收時(shí)收到一個(gè)系統(tǒng)的通知。
總結(jié)
對(duì)于軟引用和弱引用华望,被GC標(biāo)記為垃圾準(zhǔn)備回收時(shí)會(huì)清除對(duì)應(yīng)的引用蕊蝗,即get方法返回null,然后放入綁定的引用隊(duì)列中立美,理論上此時(shí)沒有真正被回收匿又,只是沒有辦法在訪問到方灾;但是對(duì)于虛引用則是在發(fā)生回收時(shí)放入隊(duì)列建蹄,也是唯一一種確認(rèn)對(duì)象被回收而加入隊(duì)列的引用,可以利用這一特性做一些有趣的事裕偿。
finalize()越獄
通過一條到墻外的地下通道(finalize)洞慎、一列快速列車(F-Queue)的驚險(xiǎn)越獄大片
- 可達(dá)性分析發(fā)現(xiàn)這個(gè)對(duì)象沒有到GC Roots的引用鏈(快到砍頭的時(shí)候了),進(jìn)行第一次標(biāo)記嘿棘,如果對(duì)象沒有覆蓋Object的finalize方法劲腿,或者已經(jīng)執(zhí)行過了一次finalize方法,則這個(gè)對(duì)象的finalize不會(huì)執(zhí)行鸟妙,等待下一次GC被回收(蠢蛋和倒霉蛋的組合焦人,一個(gè)不知道這個(gè)秘密通道,一個(gè)越獄失敗被抓了回來成為重點(diǎn)照顧對(duì)象重父,沒日沒夜的毒打花椭,只能等死了)
- 如果覆蓋了finalize方法但還沒有執(zhí)行(已經(jīng)踩好點(diǎn)了,還在做最后的精密計(jì)劃房午,天一黑矿辽,就行動(dòng)),將這個(gè)對(duì)象放到F-Queue中等待執(zhí)行finalize方法(通過地下秘密通道到達(dá)獄外郭厌,此時(shí)列車也到了袋倔,正在排隊(duì)刷卡上車...)
- 以低優(yōu)先級(jí)執(zhí)行F-Queue中對(duì)象的finalize方法,進(jìn)行第二次標(biāo)記折柠,如果finalize中將自己對(duì)象的引用與外面的引用鏈上的對(duì)象建立了鏈接宾娜,則將其移出F-Queue,成功逃脫回收(滴扇售,學(xué)生卡前塔,上車落座贾陷,系好安全帶,老司機(jī)要飆車了)嘱根,如果沒有建立與引用鏈的鏈接髓废,則等待下一次GC被回收(滴,余額不足该抒,請(qǐng)及時(shí)充值慌洪,司機(jī)說窮鬼,沒錢還想做快速列車凑保。哎哎哎~冈爹,老司機(jī)帶帶我呀,帶帶我呀欧引,帶我呀频伤,我呀,呀...曾經(jīng)的秋名山車神遺棄在角落芝此。)
進(jìn)行了兩次標(biāo)記:第一次標(biāo)記是篩選有哪些finalize方法是需要執(zhí)行的憋肖;第二次標(biāo)記是哪些finalize方法關(guān)聯(lián)了引用鏈,將其移出隊(duì)列婚苹。
public class SaveSelfTest {
public static Scofield BreakAway= null;//逃生專列
public static void main(String[] args) throws Exception{
Scofield michael = new Scofield();
michael.sayHi();
//第一次逃脫岸更,成功
michael = null;//置為null,觸發(fā)回收的條件
System.gc();
Thread.sleep(200);
if(BreakAway != null){
BreakAway.sayHi();
}else{
util.print("ScoField:Please save me,I'll dead next morning!");
}
//第二次逃脫膊升,失敗怎炊,finalize函數(shù)只能最多被執(zhí)行一次
BreakAway = null;
System.gc();
Thread.sleep(200);
if(BreakAway != null){
BreakAway.sayHi();
}else{
util.print("ScoField:Please save me,I'll dead next morning!");
}
System.exit(0);
}
}
class Scofield{
public void sayHi(){
util.print("Hi, I'm Michael ScoField!");
}
@Override
protected void finalize() throws Throwable{
super.finalize();//Object
util.print("Scofield gets on car!");
SaveSelfTest.BreakAway = this;//關(guān)聯(lián)引用鏈
}
}
class util{
public static void print(String str){
System.out.println(str);
}
}
/* Output
Hi, I'm Michael ScoField!
Scofield gets on car!
Hi, I'm Michael ScoField!
ScoField:Please save me,I'll dead next morning!
*/
參考鏈接
http://www.cnblogs.com/jiangyi-uestc/p/5679331.html
http://www.importnew.com/14115.html