java,內存,存儲

 1.

 

有這樣一種說法,如今爭鋒於IT戰場的兩大勢力,MS一族偏重於底層實現,Java一族偏重於系統架構。說法根據無從考證,但從兩大勢力各自的社區力量和圖書市場已有佳作不難看出,此說法不虛,但掌握Java的底層實現對Java程序員來說是至關重要的,本文介紹了Java中的數據在內存中的存儲。
  
  2 內存中的堆(stack)與棧(heap)
  Java程序運行時有6個地方可以存儲數據,它們分別是寄存器、棧、堆、靜態存儲、常量存儲和非RAM存儲,主要是堆與棧的存儲。

 

     【隨機存儲器 :Random Access Memory 】


  棧與堆都是Java用來在RAM中存放數據的地方。與C++不同,Java自動管理棧和堆,程序員不能直接地設置棧或堆。棧的優勢是,存取速度比堆要快,僅次於直接位於CPU中的寄存器。另外,棧數據可以共享。但缺點是,存在棧中的數據大小與生存期必須是確定的,缺乏靈活性。堆的優勢是可以動態地分配內存大小,生存期也不必事先告訴編譯器,Java的垃圾收集器會自動收走這些不再使用的數據。但缺點是,由於要在運行時動態分配內存,存取速度較慢


  【 寄存器位於CPU中 】


  3 Java中數據在內存中的存儲
  3.1基本數據類型的存儲
  Java的基本數據類型共有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。在編譯器內部,遇到時,它就會重新搜索棧中是否有4的字面值,如果沒有,重新開闢地址存放4的值;如果已經有了,則直接將a指向這個地址。因此a值的改變不會影響到b的值。

    【定義變量,給變量賦值,然後在編譯的過程中就可以將其保存在內存中了】

 
  3.2對象的內存模型
  在Java中,創建一個對象包括對象的聲明和實例化兩步,下面用一個例題來說明對象的內存模型。
  假設有類Rectangle定義如下:

    【Rectangle:矩形
  class Rectangle{
  double width,height;
  Rectangle(double w,double h){
  width=w;height=h; }}
  (1)聲明對象時的內存模型
  用Rectangle rect;聲明一個對象rect時,將在棧內存爲對象的引用變量rect分配內存空間,但Rectangle的值爲空,稱rect是一個空對象。空對象不能使用,因爲它還沒有引用任何“實體”。
  (2)對象實例化時的內存模型
  當執行rect=new Rectangle(3,5);時,會做兩件事
  在堆內存中爲類的成員變量width,height分配內存,並將其初始化爲各數據類型的默認值;接着進行顯式初始化(類定義時的初始化值);最後調用構造方法,爲成員變量賦值。
  返回堆內存中對象的引用(相當於首地址)給引用變量rect,以後就可以通過rect來引用堆內存中的對象了。

    (他奶奶的,不是很理解這兩句話!)


  (3)創建多個不同的對象實例
  一個類通過使用new運算符可以創建多個不同的對象實例,這些對象實例將在堆中被分配不同的內存空間,改變其中一個對象的狀態不會影響其他對象的狀態。例如:
  Rectangle r1=new Rectangle(3,5);
  Rectangle r2=new Rectangle(4,6);
  此時,將在堆內存中分別爲兩個對象的成員變量width、height分配內存空間,兩個對象在堆內存中佔據的空間是互不相同的。如果有
  Rectangle r1=new Rectangle(3,5);
  Rectangle r2=r1;
  則在堆內存中只創建了一個對象實例,在棧內存中創建了兩個對象引用,兩個對象引用同時指向一個對象實例。
  3.3包裝類數據的存儲
  基本型別都有對應的包裝類:如int對應Integer類,double對應Double類等,基本類型的定義都是直接在棧中,如果用包裝類來創建對象,就和普通對象一樣了。例如:int i=0;i直接存儲在棧中。  Integer i(i此時是對象) = new Integer(5);這樣,i對象數據存儲在堆中,i的引用存儲在棧中,通過棧中的引用來操作對象。

    【數據存儲在堆中,引用存儲在棧中】


  3.4 String 類型數據的存儲
  String是一個特殊的包裝類數據。

可以用     String str = new String("abc");的形式來創建;

也可以用   String str = "abc";的形式來創建。

 

第一種創建方式,和普通對象的的創建過程一樣;

第二種創建方式,Java內部將此語句轉化爲以下幾個步驟:
  (1)先定義一個名爲str的對String類的對象引用變量:String str;
  (2)在棧中查找有沒有存放值爲“abc”的地址,如果沒有,則開闢一個存放字面值爲“abc”的地址,接着創建一個新的String類的對象o,並將o的字符串值指向這個地址,而且在棧中這個地址旁邊記下這個引用的對象o。如果已經有了值爲“abc”的地址,則查找對象o,並返回o的地址。
  (3)將str指向對象o的地址


  值得注意的是,一般String類中字符串值都是直接存值的。但像String str = "abc";這種場合下,其字符串值卻是保存了一個指向存在棧中數據的引用。
  爲了更好地說明這個問題,我們可以通過以下的幾個代碼進行驗證。
  String str1=“abc”;
  String str2=“abc”;
  System.out.println(s1==s2);//true
  注意,這裏並不用str1.equals(str2);的方式,因爲這將比較兩個字符串的值是否相等。==號,根據JDK的說明,只有在兩個引用都指向了同一個對象時才返回真值。而我們在這裏要看的是,str1與str2是否都指向了同一個對象。
  我們再接着看以下的代碼。
  Stringstr1=new String(“abc”);
  Stringstr2=“abc”;
  System.out.println(str1==str2);//false
  創建了兩個引用。創建了兩個對象。兩個引用分別指向不同的兩個對象。
  以上兩段代碼說明,只要是用new()來新建對象的,都會在堆中創建,而且其字符串是單獨存值的,即使與棧中的數據相同,也不會與棧中的數據共享。
  3.5數組的內存分配
  當定義一個數組,int x[];或int []x;時,在棧內存中創建一個數組引用,通過該引用(即數組名)來引用數組。x=new int[3];將在堆內存中分配3個保存int型數據的空間,堆內存的首地址放到棧內存中,每個數組元素被初始化爲0。
  
  4 內存空間的釋放
  棧上變量的生存時間受限於當前函數的生存時間,函數退出了,變量就不存在了。在堆中分配的對象實例,當不再有任何一個引用變量指向它時,這個對象就可以被垃圾回收機制回收了。
  
  5 總結堆棧

  再來看Java的內存,棧內存用來存放一些基本類型的變量和數組及對象的引用變量,而堆內存主要是來放置對象實例的。明白這個就能很好的解釋多態、繼承、覆蓋方面的問題了

 

 

引用地址:http://blog.sina.com.cn/s/blog_5d7cccb90100bv5u.html

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章