1. 程式人生 > >Java物件建立過程和記憶體結構分析

Java物件建立過程和記憶體結構分析

JAVA記憶體分配和管理是JAVA的核心技術之一,在看了尚矽谷宋紅康老師講解的JAVA記憶體知識之後,結合《深入理解JVM這本書》對自己所學的知識進行簡單的總結,寫了這篇日誌。

1.JAVA記憶體分割槽

 根據儲存資料的不同,java記憶體通常被劃分為5個區域:程式計數器(Program Count Register)、本地方法棧(Native Stack)、方法區(Methon Area)、棧(Stack)、堆(Heap)。

程式計數器(Program Count Register):又叫程式暫存器。JVM支援多個執行緒同時執行,當每一個新執行緒被建立時,它都將得到它自己的PC暫存器(程式計數器)。如果執行緒正在執行的是一個Java方法(非native),那麼PC暫存器的值將總是指向下一條將被執行的指令,如果方法是 native的,程式計數器暫存器的值不會被定義。 JVM的程式計數器暫存器的寬度足夠保證可以持有一個返回地址或者native的指標。

棧(Stack):又叫堆疊。JVM為每個新建立的執行緒都分配一個棧。也就是說,對於一個Java程式來說,它的執行就是通過對棧的操作來完成的。棧以幀為單位儲存執行緒的狀態。JVM對棧只進行兩種操作:以幀為單位的壓棧和出棧操作。我們知道,某個執行緒正在執行的方法稱為此執行緒的當前方法。我們可能不知道,當前方法使用的幀稱為當前幀。當執行緒啟用一個Java方法,JVM就會線上程的 Java堆疊裡新壓入一個幀,這個幀自然成為了當前幀。在此方法執行期間,這個幀將用來儲存引數、區域性變數、中間計算過程和其他資料。從Java的這種分配機制來看,堆疊又可以這樣理解:棧(Stack)是作業系統在建立某個程序時或者執行緒(在支援多執行緒的作業系統中是執行緒)為這個執行緒建立的儲存區域,該區域具有先進後出的特性。

堆(Heap):Java堆(Java Heap)是Java虛擬機器所管理的記憶體中最大的一塊。Java堆是被所有執行緒共享的一塊記憶體區域。在此區域的唯一目的就是存放物件例項,幾乎所有的物件例項都是在這裡分配記憶體,但是這個物件的引用卻是在棧(Stack)中分配。因此,執行String s = new String("s")時,需要從兩個地方分配記憶體:在堆中為String物件分配記憶體,在棧中為引用(這個堆物件的記憶體地址,即指標)分配記憶體,如下圖所示。


方法區(Method Area):當虛擬機器裝載一個class檔案時,它會從這個class檔案包含的二進位制資料中解析型別資訊,然後把這些型別資訊(包括類資訊、常量、靜態變數等)放到方法區中,該記憶體區域被所有執行緒共享,如下圖所示。本地方法區存在一塊特殊的記憶體區域,叫常量池(Constant Pool),這塊記憶體將與String型別的分析密切相關。


2.JAVA物件建立過程分析

以建立Person物件為例,分析物件建立過程中在記憶體中的分配情況以及方法的呼叫過程。

class Person 
	{ 
	   private String name; 
	   private int age; 

	   DemoTest dTest = new DemoTest();
	   public Person(String name, int age) 
	  { 
	       System.out.println("這是person的建構函式"); 
	       this.name = name; 
	       this.age = age; 
	   } 


	  { 
	       System.out.println("這是person的構造程式碼塊"); 
	   } 


	   static  
	  { 
	       System.out.println("這是person類的靜態程式碼塊"); 
	   }    
	   public void setName(String name) 
	  {
	       this.name = name; 
	   } 


	   public void show() 
	  { 
	      System.out.println("name = "+name + "::"+"age = "+age); 
	   } 

	} 


	class DemoTest 
	{ 
	     public DemoTest() 
	    { 
	         System.out.println("這是一個測試的類"); 
	     } 
	} 


	public class test 
	{ 
	     public static void main(String[] args) 
	    { 
	         Person p = new Person("gao",24); 
	         p.setName("wz"); 
	         p.show(); 
	     } 
	} 
輸出結果這是person類的靜態程式碼塊 
    這是一個測試的類 
            這是person的構造程式碼塊 
    這是person的建構函式 
    name = wz::age = 20

首先棧中的main函式執行Person = new Person("gao",24);這個簡單的語句會涉及到如下幾個步驟: 
1,由於是要建立Person類物件,java虛擬機器(JVM)先去找Person.class檔案,如果有的話,將其載入到記憶體。 
2,將型別資訊(包括靜態變數,方法等)載入進方法區。 
3,執行該類中static程式碼塊,如果有的話,對Person.class類進行初始化。(此時輸出‘這是person類的靜態程式碼塊’) 
4,到這時才進行堆記憶體空間的開闢,併為物件分配首地址。 
5,在堆記憶體中建立物件的成員屬性,並對其進行初始化(先進行預設初始化再進行顯示初始化。(此時輸出  ‘這是一個測試的類’) 
6,進行構造程式碼塊的初始化,由此看出構造程式碼庫初始化的優先順序要高於物件建構函式的初始化。 (此時輸出 ‘這是person的構造程式碼塊’) 
7,物件的建構函式進行初始化。(此時輸出 ‘這是person的建構函式’); 
8,將堆記憶體中的地址賦給棧記憶體中的p變數。

以上是我的java虛擬機器機制的一些理解,由於JVM的複雜性,完全理解它是很困難的,如果文章中有錯誤的還望大家指出。