Jvm方法區以及static的內存分配圖


 

前面的幾篇都沒有太明確地指出 方法區 是什么?現在通過一些資料的收集和學習,下面做一些總結

什么是方法區:

 

方法區是系統分配的一個內存邏輯區域,是JVM在裝載類文件時,用於存儲類型信息的(類的描述信息)。

 

方法區存放的信息包括:

類的基本信息:

1.每個類的全限定名

2.每個類的直接超類的全限定名(可約束類型轉換)

3.該類是類還是接口

4.該類型的訪問修飾符

5.直接超接口的全限定名的有序列表

已裝載類的詳細信息

1.      運行時常量池:在方法區中,每個類型都對應一個常量池,存放該類型所用到的所有常量,常量池中存儲了諸如文字字符串、final變量值、類名和方法名常量。

2.      字段信息:字段信息存放類中聲明的每一個字段的信息,包括字段的名、類型、修飾符。

3.      方法信息:類中聲明的每一個方法的信息,包括方法名、返回值類型、參數類型、修飾符、異常、方法的字節碼。

(在編譯的時候,就已經將方法的局部變量、操作數棧大小等確定並存放在字節碼中,在裝載的時候,隨着類一起裝入方法區。)

4.      靜態變量:類變量,類的所有實例都共享,我們只需知道,在方法區有個靜態區,靜態區專門存放靜態變量和靜態塊。

5.      到類classloader的引用:到該類的類裝載器的引用。

6.      到類class 的引用:虛擬機為每一個被裝載的類型創建一個class實例,用來代表這個被裝載的類。 

下面分析static的內存分配

 

  1.  
    public class Dome_Static {
  2.  
     
  3.  
    public static void main(String[] args) {
  4.  
    Person p1 = new Person();
  5.  
    p1.name = "xiaoming";
  6.  
    p1.country = "chinese";
  7.  
    Person p2 = new Person();
  8.  
    p2.name = "xiaohong";
  9.  
    p1.speak();
  10.  
    p2.speak();
  11.  
    }
  12.  
     
  13.  
    }
  14.  
    class Person {
  15.  
    String name;
  16.  
    static String country;
  17.  
    public void speak() {
  18.  
    System.out.println( "name:"+name+ ",country:"+country);
  19.  
    }
  20.  
    }

 

  1.  
    Output:
  2.  
    name:xiaoming,country:chinese
  3.  
    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時,可通過該堆內存對象找到對應的類,讀取對應靜態區中的字段值

 

最后給大家一道面試題練練手,要求寫出其結果(筆試)

 

  1.  
    public class StaticTest {
  2.  
        
  3.  
        public static int k = 0;
  4.  
        public static StaticTest t1 = new StaticTest( "t1");
  5.  
        public static StaticTest t2 = new StaticTest( "t2");
  6.  
        public static int i = print( "i");
  7.  
        public static int n = 99;
  8.  
        public int j = print( "j");
  9.  
         
  10.  
        {
  11.  
            print( "構造塊");
  12.  
        }
  13.  
         
  14.  
        static{
  15.  
            print( "靜態塊");
  16.  
        }
  17.  
         
  18.  
        public StaticTest(String str) {
  19.  
            System.out.println((++k) + ":" + str + " i=" + i + " n=" + n);
  20.  
            ++n;
  21.  
            ++i;
  22.  
        }
  23.  
         
  24.  
        public static int print(String str) {
  25.  
            System.out.println((++k) + ":" + str + " i=" + i + " n=" + n);
  26.  
            ++i;
  27.  
            return ++n;
  28.  
        }
  29.  
        public static void main(String[] args) {
  30.  
            StaticTest t = new StaticTest( "init");
  31.  
        }
  32.  
     
  33.  
    }


結果:

 

 

  1.  
    1:j i= 0 n= 0
  2.  
    2:構造塊 i= 1 n= 1
  3.  
    3:t1 i= 2 n= 2
  4.  
    4:j i= 3 n= 3
  5.  
    5:構造塊 i= 4 n= 4
  6.  
    6:t2 i= 5 n= 5
  7.  
    7:i i= 6 n= 6
  8.  
    8:靜態塊 i= 7 n= 99
  9.  
    9:j i= 8 n= 100
  10.  
    10:構造塊 i= 9 n= 101
  11.  
    11:init i= 10 n= 102


這個留給大家去思考,如果一眼便能便知道為什么是這樣的輸出結果,那么靜態方面知識應該比較扎實了

 

提示一下 :

1.加載的順序:先父類的static成員變量 -> 子類的static成員變量 -> 父類的成員變量 -> 父類構造 -> 子類成員變量 -> 子類構造

2.static只會加載一次,所以通俗點講第一次new的時候,所有的static都先會被全部載入(以后再有new都會忽略),進行默認初始化。在從上往下進行顯示初始化。這里靜態代碼塊和靜態成員變量沒有先后之分,誰在上,誰就先初始化

3.構造代碼塊是什么?把所有構造方法中相同的內容抽取出來,定義到構造代碼塊中,將來在調用構造方法的時候,會去自動調用構造代碼塊。構造代碼快優先於構造方法。

轉載自:https://blog.csdn.net/Wang_1997/article/details/52267688


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM