個人理解:內存中的棧和堆

內存中的棧和堆

總結

個人理解,注意好棧區能存什麼類型值,堆區存什麼值,再者分清基本數據類型和引用數據類型就ok了。

內存中的堆和棧

> 棧區和堆區
    一: 基本概念
    1、java中的棧(stack)和堆(heap)是java在內存(ram)中存放數據的地方

    2、堆區

     存儲的全部是對象,每個對象都包含一個與之對應的class的信息。(class的目的是得到操作指令);

     jvm只有一個heap區,被所有線程共享,不存放基本類型和對象引用,只存放對象本身。

     堆的優劣勢:堆的優勢是可以動態的分配內存大小,生存期也不必事先告訴編譯器,java的垃圾收集器會自動收取這些不在使用的數               

        據,但缺點是,由於要在運行時動態分配內存,存取速度慢。

    3、棧區

     每一個線程包含一個stack區,只保存基本數據類型的對象和自定義對象的引用(不是對象),對象都存放在共享heap中;

     每個棧中的數據(基本數據類型和對象引用)都是私有的,其他棧不能訪問;

     棧分爲3部分:基本類型變量區、執行環境上下文、操作指令區(存放操作指令)

  棧的優勢劣勢:存取速度比堆要快,僅次於直接位於CPU的寄存器,但必須確定的是存在stack中的數據大小與生存期必須是確定          

          的,缺乏靈活性。單個stack的數據可以共享。

      stack:是一個先進後出的數據結構,通常保存方法中的參數,局部變量。

      在java中,所有基本類型和引用類型都在stack中儲存,棧中數據的生存空間一般在當前scopes內

    4、方法區

     1、又叫靜態區,跟堆一樣,被所有的線程共享。方法區包含所有的classstatic變量;

     2、方法區中包含的都是在程序中永遠的唯一的元素

    二: Java中的數據類型有兩種。

  一種是基本類型(primitive types), 共有8種,即int, short, long, byte, float, double, boolean, char(注意, 並沒有string的基本類型)。這種類型的定義是通過諸如int a = 3; long b = 255L;的形式來定義的,稱爲自動變量。值得注意的是,自動變量存的是字面值,不是類的實例,即不是類的引用,這裏並沒有類的存在。如int a = 3; 這裏的a是一個指向int類型的引用,指向3這個字面值。這些字面值的數據,由於大小可知,生存期可知(這些字面值固定定義在某個程序塊裏面,程序塊退出後,字段值就消失了),出於追求速度的原因,就存在於棧中。 
  另外,棧有一個很重要的特殊性,就是存在棧中的數據可以共享。假設我們同時定義:

        int a = 3;  
        int b = 3;

  編譯器先處理int a = 3;首先它會在棧中創建一個變量爲a的引用,然後查找有沒有字面值爲3的地址,沒找到,就開闢一個存放3這個 字面值的地址,然後將a指向3的地址。接着處理int b = 3;在創建完b的引用變量後,由於在棧中已經有3這個字面值,便將b直接指向3的地址 。這樣,就出現了a與b同時均指向3的情況。 
  特別注意的是,這種字面值的引用與類對象的引用不同。假定兩個類對象的引用同時指向一個對象,如果一個對象引用變量修改了這個 對象的內部狀態,那麼另一個對象引用變量也即刻反映出這個變化。相反,通過字面值的引用來修改其值,不會導致另一個指向此字面值的 引用的值也跟着改變的情況。如上例,我們定義完a與b的值後,再令a=4;那麼,b不會等於4,還是等於3。在編譯器內部,遇到a=4;時,它 就會重新搜索棧中是否有4的字面值,如果沒有,重新開闢地址存放4的值;如果已經有了,則直接將a指向這個地址。因此a值的改變不會影響到b的值。 
  另一種是包裝類數據,如Integer, String, Double等將相應的基本數據類型包裝起來的類。這些類數據全部存在於堆中,Java用new() 語句來顯示地告訴編譯器,在運行時才根據需要動態創建,因此比較靈活,但缺點是要佔用更多的時間。 
  
  
    三: 實例探索
    示例1

    main()
      int x=1;
    show ()
      int x=2

    主函數main()中定義變量int x=1,show()函數中定義變量int x=1。最後show()函數執行完畢。


    以上程序執行步驟:

    第1步——main()函數是程序入口,JVM先執行,在棧內存中開闢一個空間,存放int類型變量x,同時附值1。
    第2步——JVM執行show()函數,在棧內存中又開闢一個新的空間,存放int類型變量x,同時附值2。
         此時main空間與show空間並存,同時運行,互不影響。
    第3步——show()執行完畢,變量x立即釋放,空間消失。但是main()函數空間仍存在,main中的變量x仍然存在,不受影響。

    示意圖如下:
    ![image](https://images2015.cnblogs.com/blog/1099884/201702/1099884-20170208230951901-930534799.jpg)

    示例2

    main()
      int[] x=new int[3];
      x[0]=20

    主函數main()中定義數組x,元素類型int,元素個數3。

    以上程序執行步驟
    第1步——執行int[] x=new int[3];
      隱藏以下幾分支
      JVM執行main()函數,在棧內存中開闢一個空間,存放x變量(x變量是局部變量)。
      同時,在堆內存中也開闢一個空間,存放new int[3]數組,堆內存會自動內存首地址值,如0x0045。
      數組在棧內存中的地址值,會附給x,這樣x也有地址值。所以,x就指向(引用)了這個數組。此時,所有元素均未附值,但都有默認初始化值0。

    第2步——執行x[0]=20
      即在堆內存中將20附給[0]這個數組元素。這樣,數組的三個元素值分別爲20,0,0

    示圖如下:
    ![image](https://images2015.cnblogs.com/blog/1099884/201702/1099884-20170208231402666-566810794.jpg)

    示例3

    main()
      int[] x=new int[3];
      x[0]=20
      x=null;

    以上步驟執行步驟
    第12步——與示例2完全一樣,略。

    第3步——執行x=null;
      null表示空值,即x的引用數組內存地址0x0045被刪除了,則不再指向棧內存中的數組。此時,堆中的數組不再被x使用了,即被視爲垃圾,JVM會啓動垃圾回收機制,不定時自動刪除。

    示圖如下:
    ![image](https://images2015.cnblogs.com/blog/1099884/201702/1099884-20170208231602291-1005997283.jpg)

    示例4
    main()
      int[] x=new int[3];
      int[] y=x;
      y[1]=100
      x=null;

    以上步驟執行步驟

    第1步——與示例21步一致,略。
    第2步——執行int[] y=x,
      在棧內存定義了新的數組變量內存y,同時將x的值0x0045附給了y。所以,y也指向了堆內存中的同一個數組。
    第3步——執行y[1]=100
      即在堆內存中將20附給[0]這個數組元素。這樣,數組的三個元素值分別爲0,100,04步——執行x=null
      則變量x不再指向棧內存中的數組了。但是,變量y仍然指向,所以數組不消失。

    示圖如下
    ![image](https://images2015.cnblogs.com/blog/1099884/201702/1099884-20170208231954541-572479628.jpg)

    示例5

    Car c=new Car;
    c.color="blue";
    Car c1=new Car;
    c1.num=5;

     雖然是個對象都引用new Car,但是是兩個不同的對象。每一次new,都產生不同的實體
     示圖如下
     ![image](https://images2015.cnblogs.com/blog/1099884/201702/1099884-20170221140004820-645574950.jpg)

     示例6

    Car c=new Car;
    c.num=5;
    Car c1=c;
    c1.color="green";
    c.run();

    Car c1=c,這句話相當於將對象複製一份出來,兩個對象的內存地址值一樣。所以指向同一個實體,對c1的屬性修改,相當於c的屬性也改了。
    示圖如下
    ![image](https://images2015.cnblogs.com/blog/1099884/201702/1099884-20170221141037179-1006525570.jpg)

    四: 棧和堆的特點
    棧:

    函數中定義的基本類型變量,對象的引用變量都在函數的棧內存中分配。
    棧內存特點,數數據一執行完畢,變量會立即釋放,節約內存空間。
    棧內存中的數據,沒有默認初始化值,需要手動設置。

    堆:

    堆內存用來存放new創建的對象和數組。
    堆內存中所有的實體都有內存地址值。
    堆內存中的實體是用來封裝數據的,這些數據都有默認初始化值。
    堆內存中的實體不再被指向時,JVM啓動垃圾回收機制,自動清除,這也是JAVA優於C++的表現之一(C++中需要程序員手動清除)。

    五: 什麼是局部變量
    定義在函數中的變量、定義在函數中的參數上的變量、定義在for循環內部的變量

JAVA在程序運行時,在內存中劃分5片空間進行數據的存儲。分別是:1:寄存器。2:本地方法區。3:方法區。4:棧。5:堆

 1. 寄存器(register)。這是最快的存儲區,因爲它位於不同於其他存儲區的地方——處理器內部。但是寄存器的數量極其有限,所    

          以寄存器由編譯器根據需求進行分配。你不能直接控制,也不能在程序中感覺到寄存器存在的任何跡象。


  2. 堆棧(stack)。位於通用RAM中,但通過它的“堆棧指針”可以從處理器哪裏獲得支持。堆棧指針若向下移動,則分配新的內存;    

    若向上移動,則釋放那些 內存。這是一種快速有效的分配存儲方法,僅次於寄存器。創建程序時候,JAVA編譯器必須知道存儲    

    在堆棧內所有數據的確切大小和生命週期,因爲它必須生成 相應的代碼,以便上下移動堆棧指針。這一約束限制了程序的靈活      

    性,所以雖然某些JAVA數據存儲在堆棧中——特別是對象引用,但是JAVA對象不存儲其 中。 


  3. 堆(heap)。一種通用性的內存池(也存在於RAM中),用於存放所以的JAVA對象。堆不同於堆棧的好處是:編譯器不需要知    

    道要從堆裏分配多少存儲區 域,也不必知道存儲的數據在堆裏存活多長時間。因此,在堆裏分配存儲有很大的靈活性。當你需要    

    創建一個對象的時候,只需要new寫一行簡單的代碼,當執行 這行代碼時,會自動在堆裏進行存儲分配。當然,爲這種靈活性必    

    須要付出相應的代碼。用堆進行存儲分配比用堆棧進行存儲存儲需要更多的時間。 


  4. 靜態存儲(static storage)。這裏的“靜態”是指“在固定的位置”。靜態存儲裏存放程序運行時一直存在的數據。你可用關鍵字    

     static來標識一個對象的特定元素是靜態的,但JAVA對象本身從來不會存放在靜態存儲空間裏。 


  5. 常量存儲(constant storage)。常量值通常直接存放在程序代碼內部,這樣做是安全的,因爲它們永遠不會被改變。有時,在    

    嵌入式系統中,常量本身會和其他部分分割離開,所以在這種情況下,可以選擇將其放在ROM中 
    


  6. 非RAM存儲。如果數據完全存活於程序之外,那麼它可以不受程序的任何控制,在程序沒有運行時也可以存在。 
   就速度來說,有如下關係: 
   寄存器 < 堆棧 < 堆 < 其他 
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章