天天看點

Java對象與記憶體配置設定

##Java對象與記憶體配置設定

Java中一切都是對象,對象是Java運作的單元,知道對象是如何存在的、什麼時候是可見的,才知道怎樣運用對象來完成相應的操作。

Java運作時對象和變量等都是在記憶體中,可以根據記憶體中的資料來判斷這些對象的可見性。下面了解一下Java對象在記憶體中的配置設定。

記憶體主要分為:程式計數器、虛拟機棧、堆、方法區、本地方法棧。

程式計數器可以看作是目前線程所執行的位元組碼的行号訓示器。它是線程私有的。

虛拟機棧也是線程私有的,描述的是Java方法執行的記憶體模型:每個方法在執行的同時都會建立一個棧幀用于存儲局部變量表、操作數棧、動态連結、方法出口等資訊。每個方法從調用到執行完成的過程,就對應着一個棧幀在虛拟機棧中入棧到出棧的過程。

本地方法棧同虛拟機棧非常相似,,為虛拟機使用到的Native方法服務。也是線程私有的。

Java堆是所有線程共享的,是Java虛拟機所管理的記憶體中最大的一塊,在虛拟機啟動時建立。幾乎所有的對象執行個體以及數組都要在堆上配置設定記憶體。Java堆是垃圾收集器管理的主要區域,是以也稱作“GC堆”。Java堆在計算機實際記憶體中可以處于實體上不連續的記憶體空間中,隻要邏輯上是連續的即可。

方法區是各個線程共享的記憶體區域,它用于存儲已被虛拟機加載的類資訊、常量、靜态變量、即時編譯器編譯後的代碼等資料。

運作時常量池是方法區的一部分。Class檔案中除了有類的版本、字段、方法、接口等描述資訊外,還有常量池,用于存放編譯期生成的各種字面量和符号引用,這部分内容将在類加載後進入方法區的運作時常量池中存放。

C語言可以直接操控電腦的記憶體,是以C語言在操作資料時要手動配置設定記憶體給資料,在資料用完後要及時釋放記憶體,以免發生記憶體溢出問題。Java是用C語言寫的,它對記憶體的配置設定和回收機制已經處理了,是以不需要程式員再去手動的處理記憶體的申請與釋放操作。

Java雖然有回收機制(GC),也不能肆無忌憚的建立對象。建的多了GC頻繁的回收也會影響效率,也會導緻記憶體溢出。了解Java對象的記憶體配置設定有助于服務性能的提高。

Java變量分為局部變量和成員變量。局部變量主要是:a、形參,即方法的參數;b、方法中的變量;c、代碼塊中的變量。成員變量主要是非靜态變量即執行個體變量和靜态變量即類變量。

變量的定義和初始化的記憶體配置設定的時機是有先後順序的,如下:

a、類變量和靜态代碼塊為同一級,優先配置設定記憶體,其次是執行個體變量和代碼塊這一級别,最後是為構造器初始化配置設定記憶體;

b、Java要求定義處在記憶體配置設定中同一級别的變量時,必須采用合法的前向引導。通俗的講就是類變量和靜态代碼塊定義和初始化的變量要有先後順序,執行個體變量和代碼塊定義和初始化要有先後順序;

c、記憶體配置設定中同一級别的變量,先按順序定義,都定義好後再按順序初始化。

下面舉例說明:

public class MemoryTest {     //第一步
    int surplus = num - count;          //a //第六步
    static int count = 0;     //第二步
    int max;  //第七步
    {  //第八步
        System.out.println("代碼塊初始化前surplus=" + surplus);
        count = 2;
        surplus = num - count;
        persons = 1;                  //b
        int var = 3;
        System.out.println("代碼塊初始化後surplus=" + surplus);
    }
    static int num = 5;     //第三步
    int persons = 2; //第九步
    {//第十步
        max = count -persons + 1;     //c
    }

    public static void main(String[] args){  //第四步
        MemoryTest memory = new MemoryTest();  //第五步
        System.out.println(memory.persons);
    }
}
           

執行結果是

代碼塊初始化前surplus=5
代碼塊初始化後surplus=3
2
           

這個例子很好的說明了變量的記憶體配置設定和配置設定時機。這裡的變量存在執行個體變量、類變量、代碼塊中的局部變量。這裡對代碼分析下記憶體配置設定和配置設定時機.

在第六步執行個體變量surplus初始化時用到了類變量num和count,num和count的定義和初始化是在後面代碼,之是以執行個體變量可以操作類變量num和count是因為類變量是屬于類本身的,就是在加載MemoryTest類時就對類變量進行定義和初始化.

類初始化時先将count和num定義到棧記憶體中,并配置設定記憶體空間,此時兩個變量的預設值都是0.再根據初始化代碼進行初始化.如圖:

Java對象與記憶體配置設定

在第五步建立類MemoryTest對象new MemoryTest()時會在堆棧中為執行個體memory配置設定記憶體并賦預設值null,再在堆記憶體中建立對象。下面用圖來分析MemoryTest對象的建立過程。

首先,在堆記憶體中配置設定空間,将對象的執行個體變量逐個定義,類型為int型,預設值都是0,如圖:

Java對象與記憶體配置設定

其次,根據代碼和變量的先後順序為變量初始化,執行個體變量和非靜态代碼塊按順序執行,如第六步,

Java對象與記憶體配置設定

在執行第八步代碼塊時,會對persons指派,雖然定義persons的代碼是在代碼塊後,但對象定義變量是在初始化和代碼塊之前執行,是以在代碼塊中為persons指派不會報錯。

Java對象與記憶體配置設定

第八步代碼塊中定義的局部變量var的作用域僅是代碼塊,代碼塊結束後var也随記被銷毀,空間被回收。

Java對象與記憶體配置設定

這裡需要注意一個問題,在第八步代碼塊中,persons隻能被指派,不能作為變量用于其他語句的計算或引用等操作,原因是persons是在第九步定義被指派,代碼現在執行在第八步,我們說執行個體變量和代碼塊是順序執行的,還未執行到第九步,也就是persons還未結束定義初始化,是以不能操作persons變量,但在第九步初始化結束,persons變量就可以使用了。

Java對象與記憶體配置設定

MemoryTest對象建立完之後memory指向對象位址,調用對象的persons執行個體變量。

Java對象與記憶體配置設定

main方法執行完之後,堆棧中的memory将被銷毀,空間回收,此時MemoryTest對象将沒有執行個體指向它,它也會被GC回收。

繼續閱讀