Java系列之 方法區以及static的記憶體分配圖
轉載自: https://blog.csdn.net/Wang_1997/article/details/52267688
前面的幾篇都沒有太明確地指出 方法區 是什麼?現在通過一些資料的收集和學習,下面做一些總結
什麼是方法區:
方法區是系統分配的一個記憶體邏輯區域,是JVM在裝載類檔案時,用於儲存型別資訊的(類的描述資訊)。
方法區存放的資訊包括:
類的基本資訊:
1.每個類的全限定名
2.每個類的直接超類的全限定名(可約束型別轉換)
3.該類是類還是介面
4.該型別的訪問修飾符
5.直接超介面的全限定名的有序列表
已裝載類的詳細資訊
1. 執行時常量池:在方法區中,每個型別都對應一個常量池,存放該型別所用到的所有常量,常量池中儲存了諸如文字字串、final變數值、類名和方法名常量。
2. 欄位資訊:欄位資訊存放類中宣告的每一個欄位的資訊,包括欄位的名、型別、修飾符。
3. 方法資訊:類中宣告的每一個方法的資訊,包括方法名、返回值型別、引數型別、修飾符、異常、方法的位元組碼。
(在編譯的時候,就已經將方法的區域性變數、運算元棧大小等確定並存放在位元組碼中,在裝載的時候,隨著類一起裝入方法區。)
4. 靜態變數:類變數,類的所有例項都共享,我們只需知道,在方法區有個靜態區,靜態區專門存放靜態變數和靜態塊。
5. 到類classloader的引用:到該類的類裝載器的引用。
6. 到類class 的引用:虛擬機器為每一個被裝載的型別建立一個class例項,用來代表這個被裝載的類。
下面分析static的記憶體分配
public class Dome_Static {
public static void main(String[] args) {
Person p1 = new Person();
p1.name = "xiaoming";
p1.country = "chinese";
Person p2 = new Person();
p2.name = "xiaohong";
p1.speak();
p2.speak();
}
}
class Person {
String name;
static String country;
public void speak() {
System.out.println("name:"+name+",country:"+country);
}
}
- Output:
- name:xiaoming,country:chinese
- name:xiaohong,country:chinese
1.首先,先載入Dome_Static,然後其main函式入棧,之後Person被載入。static宣告的變數會隨著類的載入而載入,所以在記憶體中只會存在一份,例項化多個物件,都共享同一個static變數,會預設初始化
2.在棧記憶體為 p1 變數申請一個空間,在堆記憶體為Person物件申請空間,初始化完畢後將其地址值返回給p1,通過p1.name和p1.country修改其值
3.在棧記憶體為 p2 變數申請一個空間,在堆記憶體為Person物件申請空間,初始化完畢後將其地址值返回給p2,僅僅通過p2.name修改其值
4.列印show方法,進棧,這裡就不畫圖了,對於棧相關的概念不清楚的可以看看在之前發的部落格。簡單口述下:p1.show() show方法入棧,在方法的內部有個指向堆記憶體的this引用,通過該引用可找到堆記憶體實體,列印country時,可通過該堆記憶體物件找到對應的類,讀取對應靜態區中的欄位值
最後給大家一道面試題練練手,要求寫出其結果(筆試)
public class StaticTest {
public static int k = 0;
public static StaticTest t1 = new StaticTest("t1");
public static StaticTest t2 = new StaticTest("t2");
public static int i = print("i");
public static int n = 99;
public int j = print("j");
{
print("構造塊");
}
static{
print("靜態塊");
}
public StaticTest(String str) {
System.out.println((++k) + ":" + str + " i=" + i + " n=" + n);
++n;
++i;
}
public static int print(String str) {
System.out.println((++k) + ":" + str + " i=" + i + " n=" + n);
++i;
return ++n;
}
public static void main(String[] args) {
StaticTest t = new StaticTest("init");
}
}
結果:
1:j i=0 n=0
2:構造塊 i=1 n=1
3:t1 i=2 n=2
4:j i=3 n=3
5:構造塊 i=4 n=4
6:t2 i=5 n=5
7:i i=6 n=6
8:靜態塊 i=7 n=99
9:j i=8 n=100
10:構造塊 i=9 n=101
11:init i=10 n=102
這個留給大家去思考,如果一眼便能便知道為什麼是這樣的輸出結果,那麼靜態方面知識應該比較紮實了
提示一下 :
1.載入的順序:先父類的static成員變數 -> 子類的static成員變數 -> 父類的成員變數 -> 父類構造 -> 子類成員變數 -> 子類構造
2.static只會載入一次,所以通俗點講第一次new的時候,所有的static都先會被全部載入(以後再有new都會忽略),進行預設初始化。在從上往下進行顯示初始化。這裡靜態程式碼塊和靜態成員變數沒有先後之分,誰在上,誰就先初始化
3.構造程式碼塊是什麼?把所有構造方法中相同的內容抽取出來,定義到構造程式碼塊中,將來在呼叫構造方法的時候,會去自動呼叫構造程式碼塊。構造程式碼快優先於構造方法。