一:內存結構
1:程序計數器:當前線程的執行字節碼的行號,記錄運行情況;
2:虛擬機棧:記錄當前線程的局部變量、操作數棧、方法出口等信息;
3:本地方法棧:保存本地方法的棧信息,與虛擬機棧的區別就是虛擬機棧保存java運行信息;
4:堆(分新生代,老年代):線程共享區域,存放對象實例對象,所有的java實例基本都在這裏,是gc的主要區域,又稱“GC堆”;
5:方法區(永久代):線程共享區域,保存已被虛擬機加載的類信息,常量,靜態變量等信息;
5.1:運行時常量池:方法區的一部分,存放編譯時期的字面量和符號引用。
二:GC機制
確定gc的規則之可達性分析:從用到的引用(GC ROOTS)出發:經過所有引用鏈遍歷,不能到達的對象就是可以gc的對象。
GC Roots:
- 虛擬機棧的引用
- 方法區中的靜態屬性引用
- 方法區的常量引用
- 本地方法中的引用
GC過程:未能經過可達性驗證的對象都會標記下來,篩選有必要執行finalize方法的對象放入F_QUEUE中,然後用一個線程執行,這裏的執行是僅僅觸發,並不保證能執行完全。然後對F-QUEUE中的對象進行第二次標記,逃脫的對象免遭GC;最後回收對象。
逃脫GC的例子:
package cn.wzy.ThreadScope.escape;
/**
* @author wzy 不短不長八字剛好.
* @since 2018/9/19 9:16
*/
public class Escape {
static Escape escape = null;
@Override
protected void finalize() throws Throwable {
super.finalize();
System.out.println("====gc====");
escape = this;
}
public static void main(String[] args) throws InterruptedException {
escape = new Escape();
escape = null;
System.gc();//執行上一個對象的finalize方法
Thread.sleep(5000);
if (escape != null) {
System.out.println("====alive=====");
} else {
System.out.println("=====dead=====");
}
escape = null;
System.gc();
Thread.sleep(5000);
if (escape != null) {
System.out.println("====alive=====");
} else {
System.out.println("=====dead=====");
}
}
}
輸出:
====gc====
====alive=====
=====dead=====
在執行finalize的時候發現當前對象本來是不可達的、失去聯繫的對象,執行之後變成了可達對象,那麼逃脫gc,因爲對象的finalize是隻執行一次,所以第二次未執行這個方法,未能逃過。
三:垃圾收集算法(分代收集):
1、標記-清除算法:標記之後,將標記過的對象所在區域清除。
2、複製-清除算法:分成兩塊相等的區域,gc時將存活對象複製到另外一邊,清除本塊區域;擴展成:一塊較大的eden和兩塊survivor小區域,gc的時候將兩塊用的放到另外一塊survivor上,清理其他兩塊,來回複製:當一塊survivor上存不下的時候,將拿老年代區域來做擔保。
3、標記-整理算法:將存活對象向前擠,然後清除最後一個對象以後的區域。
分代收集:新生代和老年代都在堆內存中,新生代中的對象在很多次gc結束後還存活就會升級到老年代。永久代指的是方法區中的常量對象(如字符串對象、類信息Class對象)。