哪些區域需要回收
運行時內存分為5個區域:程序計數器、虛擬機棧、本地方法棧、堆、方法區,這些區域是如何回收的?
1-程序計數器、虛擬機棧、本地方法棧
這3個區域是隨著線程而生,隨著線程而滅,棧中的棧幀數據隨著方法的調用到結束有條不紊的進行著入棧出棧,每一個棧幀中分配多少內部基本上在類結構確定下來(類加載)就已知了。內存分配和回收具有確定性,隨著方法的結束或者線程的結束后,內存自然就會被回收了。
2- java堆和方法區
一個接口中的多個實現類需要的內存可能不一樣,一個方法的多個分支需要的內存也不一樣,我們只有在程序處于執行期間才能知道會創建哪些對象,對這部分的內存分配和回收是動態的,垃圾回收主要關注的是這部分的內存。
判斷對象是“生”是“死”?
引用計數算法
java虛擬機沒有采用引用計數算法來判斷對象是否生存,主要考慮的是它很難解決對象之間的相互循環引用的問題。
可達性分析算法(Reachablity Analysis)
基本思路是:從每一個GC Roots出發找到它所有可達的對象,走過的路徑成為引用鏈,被引用鏈串起來的就是存活對象;其他的雖然存在引用關系,但是GC Roots不可達的,判定為可回收對象。、
GC Roots :可作為的對象包含以下幾種:
- 虛擬機棧(棧幀中本地變量表)中引用的對象
- 方法區中類靜態屬性引用的對象
- 方法區中常量引用的對象
- 本地方法棧中JNI(Native Method)引用的對象
引用與緩存
為了更細致的管理對象的內存,對象引用進行了擴展,分為:強引用(Strong Reference)、軟引用(Soft Reference)、弱引用(Weak Reference)、虛引用(Phantom Reference,也叫幽靈引用)。
強引用
-
Object obj = new Object();
這種通過關鍵字new出來的對象就屬于強引用,普遍存在的,只要這種引用還關聯著對象,就不會被垃圾回收器回收。 - **內存空間不夠時,不會進行垃圾回收,而是直接拋出內存溢出異常 **
軟引用
用來描述有用但是并不必需的對象,在系統將要發生內存溢出異常之前,JVM將會把這些對象標記成垃圾數據,并對這些標記的數據進行二次回收,若回收后還是沒有足夠的內存分配,才會拋出內存溢出的異常。 用法: 用來實現內存敏感的緩存
import java.lang.ref.SoftReference;
class ReferenceTest{
Object obj = new Object();//強引用
SoftReference<Object> sr = new SoftReference<Object>(obj);//關聯軟引用
// 取消強引用,只保留軟引用
obj=null;
pass(...);
//通用的處理,以及softreference的復用模式
if(sr.get() != null){
Obeject getRef = sr.get();
use(getRef);
}else{
Object newObj = new Object();
sr = new SoftReference(newObj);//重建軟引用
}
}
- 注意
1.在虛擬機內存空間足夠大的時候是不會發生內存回收的,而且GC的優先級最低,只有所有線程都停止時才有可能執行GC(即使使用System.gc()也只是告訴JVM這是一個執行GC的好時機,但是實際上JVM會自己決斷是否達到了這個條件,比如沒有線程正在執行,這是GC這個守護線程就會執行),因為GC有一定的代價。所以與軟引用關聯的對象在內存充足時是不會發生的,只有處于內存溢出的邊緣才會發生回收操作。
2.雖然與軟引用關聯的對象可能會被收回,但保存這個軟引用關系的SoftReference變量并不一定會被回收,所以出現了ReferenceQueue來管理這些變量。
弱引用
- 弱引用描述的是非必需的對象:被弱引用關聯的對象只能生存到下一次GC回收發生之前,當GC工作時,無論當前空間是否足夠,都會回收只被弱引用關聯的對象。
- 用法:一般會和ReferenceQueue進行關聯
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的作用:
//當弱引用關聯的對象被標記為垃圾,準備回收時,
//會自動把保存弱引用的對象WeakReference放到ReferenceQueue中,等待被處理
//主要目的是:引用關聯的對象被回收了,但存儲引用的對象沒有及時回收會造成
//內存膨脹
ReferenceQueue<Object> rf = new ReferenceQueue<Object>();
WeakReference<Object> wf = new WeakReference<Object>(obj,rf);
WeakReference<Object> wf_q = null;
// poll方法,刪除隊尾引用并返回
while(wf_q = rf.poll()!= null){
// do something for remove reference
}
}
}
虛引用
- 最弱的一種引用關系,無法通過虛引用來獲取對象的實例
- 用途:僅僅是在這個對象被回收時收到一個系統的通知。
總結
對于軟引用和弱引用,被GC標記為垃圾準備回收時會清除對應的引用,即get方法返回null,然后放入綁定的引用隊列中,理論上此時沒有真正被回收,只是沒有辦法在訪問到;但是對于虛引用則是在發生回收時放入隊列,也是唯一一種確認對象被回收而加入隊列的引用,可以利用這一特性做一些有趣的事。
finalize()越獄
通過一條到墻外的地下通道(finalize)、一列快速列車(F-Queue)的驚險越獄大片
- 可達性分析發現這個對象沒有到GC Roots的引用鏈(快到砍頭的時候了),進行第一次標記,如果對象沒有覆蓋Object的finalize方法,或者已經執行過了一次finalize方法,則這個對象的finalize不會執行,等待下一次GC被回收(蠢蛋和倒霉蛋的組合,一個不知道這個秘密通道,一個越獄失敗被抓了回來成為重點照顧對象,沒日沒夜的毒打,只能等死了)
- 如果覆蓋了finalize方法但還沒有執行(已經踩好點了,還在做最后的精密計劃,天一黑,就行動),將這個對象放到F-Queue中等待執行finalize方法(通過地下秘密通道到達獄外,此時列車也到了,正在排隊刷卡上車...)
- 以低優先級執行F-Queue中對象的finalize方法,進行第二次標記,如果finalize中將自己對象的引用與外面的引用鏈上的對象建立了鏈接,則將其移出F-Queue,成功逃脫回收(滴,學生卡,上車落座,系好安全帶,老司機要飆車了),如果沒有建立與引用鏈的鏈接,則等待下一次GC被回收(滴,余額不足,請及時充值,司機說窮鬼,沒錢還想做快速列車。哎哎哎~,老司機帶帶我呀,帶帶我呀,帶我呀,我呀,呀...曾經的秋名山車神遺棄在角落。)
進行了兩次標記:第一次標記是篩選有哪些finalize方法是需要執行的;第二次標記是哪些finalize方法關聯了引用鏈,將其移出隊列。
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,觸發回收的條件
System.gc();
Thread.sleep(200);
if(BreakAway != null){
BreakAway.sayHi();
}else{
util.print("ScoField:Please save me,I'll dead next morning!");
}
//第二次逃脫,失敗,finalize函數只能最多被執行一次
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;//關聯引用鏈
}
}
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