java對象在內存中的分配



ava中內存主要包含4塊,即heap(堆內存)、stack(棧內存)、data segment(靜態變量或是常量存放區)、codesegment(方法區). 
堆內存中存放的是new出的對象,new出的對象只包含成員變量。 
棧內存中:存放的是局部成員變量。對於基本的數據類型存放的是基本變量的值,而對於對象變量,存放的是堆內存的地址。 
靜態、常量區:存放的是靜態變量(類變量)或是常量。 
方法區:存放的是對象的方法。因此即使new出多個對象也是隻是存在一個方法。 

如 
A a = new A(); A中含有一個work方法,2個成員變量a,b。那麼對應的內存分配爲 
則 a是分配在棧內存中。裏面存放了一個指向堆內存中存放的new A()的地址。 
new A()會導致在堆內存中分配一塊空間,該內存中的A對象同時會含有a和b。 
work()方法會在codesegment區中分配內存。 
如果此時 A b = a;則表示把a的值複製給b,即b的值爲a中保存的地址


java程序運行時內存分配詳解 

一、  基本概念

   每運行一個java程序會產生一個java進程,每個java進程可能包含一個或者多個線程,每一個Java進程對應唯一一個JVM實例,每一個JVM實例唯一對應一個堆,每一個線程有一個自己私有的棧。進程所創建的所有類的實例(也就是對象)或數組(指的是數組的本身,不是引用)都放在堆中,並由該進程所有的線程共享。Java中分配堆內存是自動初始化的,即爲一個對象分配內存的時候,會初始化這個對象中變量。雖然Java中所有對象的存儲空間都是在堆中分配的,但是這個對象的引用卻是在棧中分配,也就是說在建立一個對象時在堆和棧中都分配內存,在堆中分配的內存實際存放這個被創建的對象的本身,而在棧中分配的內存只是存放指向這個堆對象的引用而已。局部變量 new 出來時,在棧空間和堆空間中分配空間,當局部變量生命週期結束後,棧空間立刻被回收,堆空間區域等待GC回收。

具體的概念: JVM的內存可分爲3個區: 堆(heap)、棧(stack)和方法區(method,也叫靜態區): 

堆區: 

1.存儲的全部是對象,每個對象都包含一個與之對應的class的信息(class的目的是得到操作指令) ; 
2.jvm只有一個堆區(heap),且被所有線程共享,堆中不存放基本類型和對象引用,只存放對象本身和數組本身;

棧區:  
1.每個線程包含一個棧區,棧中只保存基礎數據類型本身和自定義對象的引用; 
2.每個棧中的數據(原始類型和對象引用)都是私有的,其他棧不能訪問; 
3.棧分爲3個部分:基本類型變量區、執行環境上下文、操作指令區(存放操作指令);

方法區(靜態區):  
1.被所有的線程共享,方法區包含所有的class(class是指類的原始代碼,要創建一個類的對象,首先要把該類的代碼加載到方法區中,並且初始化)和static變量。  
2.方法區中包含的都是在整個程序中永遠唯一的元素,如class,static變量。  
 

二、實例演示

 1   AppMain.java 
 2  
 3   public   class  AppMain                //運行時, jvmappmain的代碼全部都放入方法區     
 4   {     
 5   public   static   void  main(String[] args)  //main 方法本身放入方法區。     
 6   {     
 7   Sample test1 = new  Sample( " 測試1 " );   //test1是引用,所以放到棧區裏, Sample是自定義對象應該放到堆裏面     
 8   Sample test2 = new  Sample( " 測試2 " );     
 9      
10   test1.printName();     
11   test2.printName();     
12   }     
13   }     
14      
15   public   class  Sample        //運行時, jvmappmain的信息都放入方法區     
16   {     
17   /** 範例名稱 */     
18   private String name;      //new Sample實例後, name 引用放入棧區裏, name 對應的 String 對象放入堆裏     
19      
20   /** 構造方法 */     
21   public  Sample(String name)     
22   {     
23   this .name = name;     
24   }     
25      
26   /** 輸出 */     
27   public   void  printName()   //在沒有對象的時候,print方法跟隨sample類被放入方法區裏。     
28   {     
29   System.out.println(name);     
30   }     
31   }   

      運行該程序時,首先 啓動一個Java虛擬機進程,這個進程首先從classpath中找到AppMain.class文件,讀取這個文件中的二進制數據,然後把Appmain類的類信息存放到運行時數據區的 方法區 中,這就是AppMain類的加載過程。 

   接着,Java虛擬機定位到方法區中AppMain類的Main()方法的字節碼,開始執行它的指令。這個main()方法的第一條語句就是: 

Sample test1=new Sample("測試1");

該語句的執行過程: 
    1、 Java虛擬機到方法區找到Sample類的類型信息,沒有找到,因爲Sample類還沒有加載到方法區(這裏可以看出,java中的內部類是單獨存在的,而且剛開始的時候不會跟隨包含類一起被加載,等到要用的時候才被加載)。Java虛擬機立馬加載Sample類,把Sample類的類型信息存放在方法區裏。  
    2、 Java虛擬機首先在堆區中爲一個新的Sample實例分配內存, 並在Sample實例的內存中存放一個方法區中存放Sample類的類型信息的內存地址。 
    3、 JVM的進程中,每個線程都會擁有一個方法調用棧,用來跟蹤線程運行中一系列的方法調用過程,棧中的每一個元素就被稱爲棧幀,每當線程調用一個方法的時候就會向方法棧壓入一個新幀。這裏的幀用來存儲方法的參數、局部變量和運算過程中的臨時數據。

    4、位於“=”前的Test1是一個在main()方法中定義的一個變量(一個Sample對象的引用),因此,它被會添加到了執行main()方法的主線程的JAVA方法調用棧中。而“=”將把這個test1變量指向堆區中的Sample實例。 
    5、JVM在堆區裏繼續創建另一個Sample實例,並在main方法的方法調用棧中添加一個Test2變量,該變量指向堆區中剛纔創建的Sample新實例。

    6、JVM依次執行它們的printName()方法。當JAVA虛擬機執行test1.printName()方法時,JAVA虛擬機根據局部變量test1持有的引用,定位到堆區中的Sample實例,再根據Sample實例持有的引用,定位到方法去中Sample類的類型信息,從而獲得printName()方法的字節碼,接着執行printName()方法包含的指令,開始執行。  
三、辨析

在Java語言裏堆(heap)和棧(stack)裏的區別 :

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

Java中的2種數據類型:

一種是 基本類型 (primitive types),  四類八種:整型byte shot int long 浮點型float double 邏輯型boolean 字符型chat(注意,並沒有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()語句來顯示地告訴編譯器,在運行時才根據需要動態創建,因此比較靈活,但缺點是要佔用更多的時間。 

ava中內存主要包含4塊,即heap(堆內存)、stack(棧內存)、data segment(靜態變量或是常量存放區)、codesegment(方法區). 
堆內存中存放的是new出的對象,new出的對象只包含成員變量。 
棧內存中:存放的是局部成員變量。對於基本的數據類型存放的是基本變量的值,而對於對象變量,存放的是堆內存的地址。 
靜態、常量區:存放的是靜態變量(類變量)或是常量。 
方法區:存放的是對象的方法。因此即使new出多個對象也是隻是存在一個方法。 

如 
A a = new A(); A中含有一個work方法,2個成員變量a,b。那麼對應的內存分配爲 
則 a是分配在棧內存中。裏面存放了一個指向堆內存中存放的new A()的地址。 
new A()會導致在堆內存中分配一塊空間,該內存中的A對象同時會含有a和b。 
work()方法會在codesegment區中分配內存。 
如果此時 A b = a;則表示把a的值複製給b,即b的值爲a中保存的地址


java程序運行時內存分配詳解 

一、  基本概念

   每運行一個java程序會產生一個java進程,每個java進程可能包含一個或者多個線程,每一個Java進程對應唯一一個JVM實例,每一個JVM實例唯一對應一個堆,每一個線程有一個自己私有的棧。進程所創建的所有類的實例(也就是對象)或數組(指的是數組的本身,不是引用)都放在堆中,並由該進程所有的線程共享。Java中分配堆內存是自動初始化的,即爲一個對象分配內存的時候,會初始化這個對象中變量。雖然Java中所有對象的存儲空間都是在堆中分配的,但是這個對象的引用卻是在棧中分配,也就是說在建立一個對象時在堆和棧中都分配內存,在堆中分配的內存實際存放這個被創建的對象的本身,而在棧中分配的內存只是存放指向這個堆對象的引用而已。局部變量 new 出來時,在棧空間和堆空間中分配空間,當局部變量生命週期結束後,棧空間立刻被回收,堆空間區域等待GC回收。

具體的概念: JVM的內存可分爲3個區: 堆(heap)、棧(stack)和方法區(method,也叫靜態區): 

堆區: 

1.存儲的全部是對象,每個對象都包含一個與之對應的class的信息(class的目的是得到操作指令) ; 
2.jvm只有一個堆區(heap),且被所有線程共享,堆中不存放基本類型和對象引用,只存放對象本身和數組本身;

棧區:  
1.每個線程包含一個棧區,棧中只保存基礎數據類型本身和自定義對象的引用; 
2.每個棧中的數據(原始類型和對象引用)都是私有的,其他棧不能訪問; 
3.棧分爲3個部分:基本類型變量區、執行環境上下文、操作指令區(存放操作指令);

方法區(靜態區):  
1.被所有的線程共享,方法區包含所有的class(class是指類的原始代碼,要創建一個類的對象,首先要把該類的代碼加載到方法區中,並且初始化)和static變量。  
2.方法區中包含的都是在整個程序中永遠唯一的元素,如class,static變量。  
 

二、實例演示

 1   AppMain.java 
 2  
 3   public   class  AppMain                //運行時, jvmappmain的代碼全部都放入方法區     
 4   {     
 5   public   static   void  main(String[] args)  //main 方法本身放入方法區。     
 6   {     
 7   Sample test1 = new  Sample( " 測試1 " );   //test1是引用,所以放到棧區裏, Sample是自定義對象應該放到堆裏面     
 8   Sample test2 = new  Sample( " 測試2 " );     
 9      
10   test1.printName();     
11   test2.printName();     
12   }     
13   }     
14      
15   public   class  Sample        //運行時, jvmappmain的信息都放入方法區     
16   {     
17   /** 範例名稱 */     
18   private String name;      //new Sample實例後, name 引用放入棧區裏, name 對應的 String 對象放入堆裏     
19      
20   /** 構造方法 */     
21   public  Sample(String name)     
22   {     
23   this .name = name;     
24   }     
25      
26   /** 輸出 */     
27   public   void  printName()   //在沒有對象的時候,print方法跟隨sample類被放入方法區裏。     
28   {     
29   System.out.println(name);     
30   }     
31   }   

      運行該程序時,首先 啓動一個Java虛擬機進程,這個進程首先從classpath中找到AppMain.class文件,讀取這個文件中的二進制數據,然後把Appmain類的類信息存放到運行時數據區的 方法區 中,這就是AppMain類的加載過程。 

   接着,Java虛擬機定位到方法區中AppMain類的Main()方法的字節碼,開始執行它的指令。這個main()方法的第一條語句就是: 

Sample test1=new Sample("測試1");

該語句的執行過程: 
    1、 Java虛擬機到方法區找到Sample類的類型信息,沒有找到,因爲Sample類還沒有加載到方法區(這裏可以看出,java中的內部類是單獨存在的,而且剛開始的時候不會跟隨包含類一起被加載,等到要用的時候才被加載)。Java虛擬機立馬加載Sample類,把Sample類的類型信息存放在方法區裏。  
    2、 Java虛擬機首先在堆區中爲一個新的Sample實例分配內存, 並在Sample實例的內存中存放一個方法區中存放Sample類的類型信息的內存地址。 
    3、 JVM的進程中,每個線程都會擁有一個方法調用棧,用來跟蹤線程運行中一系列的方法調用過程,棧中的每一個元素就被稱爲棧幀,每當線程調用一個方法的時候就會向方法棧壓入一個新幀。這裏的幀用來存儲方法的參數、局部變量和運算過程中的臨時數據。

    4、位於“=”前的Test1是一個在main()方法中定義的一個變量(一個Sample對象的引用),因此,它被會添加到了執行main()方法的主線程的JAVA方法調用棧中。而“=”將把這個test1變量指向堆區中的Sample實例。 
    5、JVM在堆區裏繼續創建另一個Sample實例,並在main方法的方法調用棧中添加一個Test2變量,該變量指向堆區中剛纔創建的Sample新實例。

    6、JVM依次執行它們的printName()方法。當JAVA虛擬機執行test1.printName()方法時,JAVA虛擬機根據局部變量test1持有的引用,定位到堆區中的Sample實例,再根據Sample實例持有的引用,定位到方法去中Sample類的類型信息,從而獲得printName()方法的字節碼,接着執行printName()方法包含的指令,開始執行。  
三、辨析

在Java語言裏堆(heap)和棧(stack)裏的區別 :

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

Java中的2種數據類型:

一種是 基本類型 (primitive types),  四類八種:整型byte shot int long 浮點型float double 邏輯型boolean 字符型chat(注意,並沒有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()語句來顯示地告訴編譯器,在運行時才根據需要動態創建,因此比較靈活,但缺點是要佔用更多的時間。 

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