1. 程式人生 > >java物件建立(記憶體模型)過程詳解

java物件建立(記憶體模型)過程詳解

概述

java物件建立分為兩個過程:宣告物件引用和建立物件實體。類資訊、物件引用、物件實體均在記憶體的不同區域。

記憶體結構

每一個java應用程式均會唯一的對應一個jvm例項,而這個jvm例項將會完成物件的記憶體分配、程式執行、垃圾回收等工作。JVM將其記憶體分為三個區域:方法區、棧區、堆區。

  • 方法區:是jvm在裝載類檔案時,用於儲存類的所有描述資訊的,這些資訊主要包括,類的基本資訊(訪問修飾符、類名等)、欄位資訊(修飾符、型別、欄位名)、方法資訊(修飾符、返回值型別、方法名、引數列表型別、異常、方法體位元組碼等)、常量池、靜態區、classloader以及class的引用
  • 棧區:存放區域性變數,基本資料型別和物件引用型別
  • 堆區:用於存放new產生物件的實體,每一個物件實體均會有自己的記憶體地址,一旦這個記憶體地址不被任何棧區物件引用,就會成為垃圾,隨時被gc回收

物件建立過程

package ccnu.allTest;

public class TestObject {
    public static void main(String[] args) {
        YyPpLl ypl1 = new YyPpLl();
        YyPpLl ypl2 = new YyPpLl(48);
        System.out.println((ypl1.ii == ypl2.ii) + " "
+ YyPpLl.ii); System.out.println(ypl1.str == ypl2.str); } static class YyPpLl { private String str = "ypl"; // 顯示初始化欄位 private int i; // 未顯示初始化欄位 private static int ii = 24; // 靜態欄位 private static int iii; static { // 靜態初始化塊 System.out.println("ii="
+ ii + ", iii=" + iii); } { // 例項初始化塊 // iii = 100; System.out.println("str=" + str + ",i=" + i + ",ii=" + ii + ",iii=" + iii); } public YyPpLl(){ ii = 72; System.out.println("non-arguments"); } public YyPpLl(int i){ this.i = i; System.out.println("two arguments"); } } } /* ii=24, iii=0 str=ypl,i=0,ii=24,iii=0 non-arguments str=ypl,i=0,ii=72,iii=0 two arguments true 72 true */
  • 步驟:1、將JVM載入到記憶體
    2、jvm首先檢查public類是否存在,存在就會將它載入到記憶體方法區中,否則丟擲異常,結束程序
    3、對類中靜態欄位進行初始化(顯示初始化就為指定的值,否則為預設值),接著執行靜態程式碼塊
    4、在棧中建立區域性變數,堆中建立物件實體,為堆物件實體的成員變數(非靜態)初始化,對於靜態欄位只需要從方法區的靜態區中引用其值即可,執行構造程式碼塊,接著執行建構函式(當然,呼叫順序會一直上溯到Object類,每一個構造方法的第一行為super()語句),最後將物件實體堆地址給棧中引用變數

    要注意的是,例項欄位包括自身定義的和從父類繼承下來的(即使父類的例項欄位被子類覆蓋或者被private修飾,都照樣為其分配記憶體)
    C++中的引用和JAVA的引用作對比,其實他們兩個只是“名稱”一樣,本質並沒什麼關係,C++中的引用只是給現存變數起了一個別名(引用變數只是一個符號引用而已,編譯器並不會給引用分配新的記憶體),而JAVA中的引用變數卻是真真正正的變數,具有自己的記憶體空間,只是不同的引用變數可以“指向”同一個物件而已。因此,如果要拿C++和JAVA引用物件的方式相對比,C++中的指標倒和JAVA中的引用如出一轍,畢竟,JAVA中的引用其實就是對指標的封裝