1. 程式人生 > >深入理解Java記憶體模型

深入理解Java記憶體模型

            

      Java虛擬機器的執行時記憶體按照型別可分為5部分:Java方法區、Java棧、Native方法區、Java堆和程式計數器。 其中棧和程式計數器不能跨執行緒訪問。

程式計數器:是一塊較小的記憶體空間,它可以看作是當前執行緒所執行的位元組碼的行號指示器。每條執行緒都需要有一個獨立的程式計數器,各條執行緒之間計數器互不影響,獨立儲存,而且永遠不會發生Out of Memory問題,其它四種記憶體區域都可能出現OOM現象。

Java虛擬機器棧(Java Virtual Machine Stacks)也是執行緒私有的,它的生命週期與執行緒相同, 跟C語言類似不使用new關鍵字的變數存放在棧裡。在棧裡可能出現StackOverflowException或者OOM異常。

本地方法棧: 就是Native層用C語言編寫的棧(沒呼叫new),其它跟Java棧類似。

Java堆:所有的java隊形例項、陣列都在堆上new出來,是垃圾收集器(Garbage Collection)管理的主要區域。Java堆可以處在物理不連續的記憶體空間中, 但邏輯上要是連續的。在實現時,既可以實現成固定大小的,也可以是可擴充套件的,不過當前主流的虛擬機器都是按照可擴充套件來實現的(通過-Xmx和-Xms控制)。在日常程式設計中遇到的OOM異常大都發生在Java堆。

方法區(Method Area)與Java堆一樣,是各個執行緒共享的記憶體區域,它用於儲存已被虛擬機器載入的類資訊、常量、靜態變數、即時編譯器編譯後的程式碼

等資料。

上面是JVM的記憶體模型, 那麼一個Java物件在記憶體裡分為幾部分呢?

答案:物件在記憶體中儲存的佈局可以分為3塊區域:物件頭(Header)、例項資料(Instance Data)和對齊填充(Padding)。

那麼JVM是如何回收記憶體的呢? 

一、引用計數法: 當指向的Java物件的引用計數為0時, 在下次GC回收時會被釋放。

考慮問題: 如果出現相互引用, 即A指向B,B指向A, 那麼GC能回收嗎?

   public static class ReferenceCountingGC{
	   public Object instance=null;
	   private final int  ONE_MB=1024*1024;
	   private byte[]bigSize=new byte[1*ONE_MB]; //只為了佔空間
	   public static void testGC(){
		   ReferenceCountingGC objA = new ReferenceCountingGC();
	       ReferenceCountingGC objB = new ReferenceCountingGC();
	       objA.instance = objB;
	       objB.instance = objA;
	       objA = null;
	       objB = null;
	        //假設在這行發生GC,objA和objB是否能被回收?
	       long begin = Runtime.getRuntime().totalMemory();
	       System.out.println("befor:" + begin);
	       System.gc();
	       long after = Runtime.getRuntime().totalMemory();
	       System.out.println("after:" + after);
	       System.out.println("diff:" + (after-begin));  //判斷到底釋放了多少
	   }
	}
執行結果:

befor:16252928
after:16318464
diff:65536

     GC前後差了64K位元組, 而objA和objB都大於1M位元組, 現實沒有被釋放! 所以程式設計中切忌不要相互強引用!!!

二、可達性分析演算法

基本思路就是通過一系列的稱為“GC Roots”的物件作為起始點,從這些節點開始向下搜尋,搜尋所走過的路徑稱為引用鏈(Reference Chain),當一個物件到GC Roots沒有任何引用鏈相連(用圖論的話來說,就是從GC Roots到這個物件不可達)時,則證明此物件是不可用的。如圖所示,物件object 5、object 6、object 7雖然互相有關聯,但是它們到GC Roots是不可達的,所以它們將會被判定為是可回收的物件。

在Java語言中,可作為GC Roots的物件包括下面幾種:虛擬機器棧(棧幀中的本地變量表)中引用的物件;方法區中類靜態屬性引用的物件;方法區中常量引用的物件;本地方法棧中JNI(即一般說的Native方法)引用的物件。


    關於記憶體基礎知識就說這麼多, 下一篇準備寫如何計算Java物件的大小。