java---堆、棧、常量池的存儲數據


說到Java中堆、棧和常量池,首先還是看看他們各自存放的數據類型吧!

    

   

  

  

Java的JVM的內存可分為3個區:堆(heap)、棧(stack)和方法區(method)也叫靜態存儲區。

堆區:(存放所有new出來的對象;)

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

棧區:(存放基本類型的變量數據和對象的應用,對象(new出來的對象)本身並不存在棧中,而是存放在堆中或者常量池中(字符串常量對象存放在常量池中))

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

  常量池:存放基本類型常量和字符串常量。

方法區:

1.又叫靜態區,跟堆一樣,被所有的線程共享。方法區包含所有的class和static變量。 
2.方法區中包含的都是在整個程序中永遠唯一的元素,如class,static變量名,不是常量(它在堆棧中)。

 

 
進一步解析:

 

(指數據共享時)對於棧和常量池中的數據可以共享(具有多個引用對象),對於堆中的對象不可以共享(一個引用對象)。         (指線程共享時)比如:同一個進程的多個線程堆棧共享狀況哪個描述正確?線程共享堆,但是每個線程有自己的寄存器和棧

 解析:    線程占有的都是不共享的,其中包括:棧、寄存器、狀態、程序計數器

        線程間共享的有:堆,全局變量,靜態變量,主要指共享進程的資源

        進程占有的資源有:地址空間,全局變量,打開的文件,子進程,信號量、賬戶信息。

棧中的數據大小和生命周期是可以確定的,當沒有引用指向數據時,這個數據就會自動消失。堆中的對象的由垃圾回收器負責回收,因此大小和生命周期不需要確定,具有很大的靈活性。

而對於字符串來說,其對象的引用都是存儲在棧中的,如果是編譯期已經創建好(即指用雙引號定義的)的就存儲在常量池中,如果是運行期(new出來的對象)則存儲在堆中。對於equals相等的字符串,在常量池中是只有一份的,在堆中則有多份。

關於:方法區理解
在一個jvm實例的內部,類型信息被存儲在一個稱為方法區的內存邏輯區中。類型信息是由類加載器在類加載時從類文件中提取出來的。類(靜態)變量也存儲在方法區中。 

因為方法區是被所有線程共享的,所以必須考慮數據的線程安全。假如兩個線程都在試圖找lava的類,在lava類還沒有被加載的情況下,只應該有一個線程去加載,而另一個線程等待。 

方法區的大小不必是固定的,jvm可以根據應用的需要動態調整。同樣方法區也不必是連續的。方法區可以在堆(甚至是虛擬機自己的堆)中分配。jvm可以允許用戶和程序指定方法區的初始大小,最小和最大尺寸。 

方法區同樣存在垃圾收集,因為通過用戶定義的類加載器可以動態擴展Java程序,一些類也會成為垃圾。jvm可以回收一個未被引用的(沒有實例對象)類所占的空間,以使方法區的空間最小。 
 
方法區存在類成員表的理解
Java方法調用面臨兩個新的問題是方法重載和方法重寫,方法重載使用的技術叫符號毀壞,方法重寫的技術叫做動態派發。Java中的所有方法都支持重寫,要實現在運行時動態確定調用的具體方法就需要一張方法表,這張方法表記錄當前對象所對應的類含有的所有方法包括父類的方法。方法調用的時候需要從子類方法開始搜索這張表,如果子類未找到就到父類找,再父類。。。;符號毀壞沿用了C++的解決方法,就是給方法重命名如method+參數個數(args) 這樣方法就唯一了。所以核心問題還是方法重寫,也就是動態派發,也是java的性能瓶頸,因為C++只有virtual方法才會產生動態派發。

要處理好動態派發,一種可能的實現方式是專門開辟一個區,單獨管理所有方法,可以按照穩定性對於該對象的所有方法(當然包括父類方法)進行穩定性排序,使相同方法聚集在一起。當方法調用時在方法區搜索的時候一次定位到相同方法的位置的起始處,不管第一個命中方法是父類還是子類,也不管此處相同方法的個數,始終執行第一個。


作者:尋寒
鏈接:https://www.zhihu.com/question/23599282/answer/25076568
來源:知乎
著作權歸作者所有,轉載請聯系作者獲得授權。

舉個例子吧!

   

  Java代碼

  String s1 = "china";

  String s2 = "china";

  String s3 = "china";

  String ss1 = new String("china");

  String ss2 = new String("china");

  String ss3 = new String("china");

 

Java堆、棧和常量池

對於基礎類型的變量和常量:變量和引用存儲在棧中,常量存儲在常量池中。

如以下代碼:

  Java代碼

  int i1 = 9;

  int i2 = 9;

  int i3 = 9;

  public static final int INT1 = 9;

  public static final int INT2 = 9;

  public static final int INT3 = 9;

Java堆、棧和常量池

對於成員變量和局部變量:成員變量就是方法外部,類的內部定義的變量;局部變量就是方法或語句塊內部定義的變量。局部變量必須初始化。

  形式參數是局部變量,局部變量的數據存在於棧內存中。棧內存中的局部變量隨着方法的消失而消失。

  成員變量存儲在堆中的對象里面,由垃圾回收器負責回收。

  如以下代碼:

  Java代碼

  class BirthDate {

  private int day;

  private int month;

  private int year;

  public BirthDate(int d, int m, int y) {

  day = d;

  month = m;

  year = y;

  }

  省略get,set方法………

  }

public class Test{

  public static void main(String args[]){

  int date = 9;

  Test test = new Test();

  test.change(date);

  BirthDate d1= new BirthDate(7,7,1970);

  }

  public void change1(int i){

  i = 1234;

  }

Java堆、棧和常量池

對於以上這段代碼,date為局部變量,i,d,m,y都是形參為局部變量,day,month,year為成員變量。下面分析一下代碼執行時候的變化:

  1. main方法開始執行:int date = 9;

  date局部變量,基礎類型,引用和值都存在棧中。

  2. Test test = new Test();

  test為對象引用,存在棧中,對象(new Test())存在堆中。

  3. test.change(date);

  i為局部變量,引用和值存在棧中。當方法change執行完成后,i就會從棧中消失。

  4. BirthDate d1= new BirthDate(7,7,1970);

  d1為對象引用,存在棧中,對象(new BirthDate())存在堆中,其中d,m,y為局部變量存儲在棧中,且它們的類型為基礎類型,因此它們的數據也存儲在棧中。 day,month,year為成員變量,它們存儲在堆中(new BirthDate()里面)。當BirthDate構造方法執行完之后,d,m,y將從棧中消失。

  5.main方法執行完之后,date變量,test,d1引用將從棧中消失,new Test(),new BirthDate()將等待垃圾回收。


免責聲明!

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



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