java的堆和棧和方法區的區別

直接上圖:

 

 

堆和棧很好理解,這裏講下方法區

方法區通俗點理解就是虛擬機在完成類裝載操作後,將class文件中的常量池載入到內存中,並保存在方法區中,

方法區的好處:

常量池是爲了避免頻繁的創建和銷燬對象而影響系統性能,其實現了對象的共享。
例如字符串常量池,在編譯階段就把所有的字符串文字放到一個常量池中。
(1)節省內存空間:常量池中所有相同的字符串常量被合併,只佔用一個空間。
(2)節省運行時間:比較字符串時,==比equals()快。對於兩個引用變量,只用==判斷引用是否相等,也就可以判斷實際值是否相等。

 

以  MyTest下面的爲列:

 1 String s1 = "Hello";
 2 String s2 = "Hello";
 3 String s3 = "Hel" + "lo";
 4 String s4 = "Hel" + new String("lo");
 5 String s5 = new String("Hello");
 6 String s6 = s5.intern();
 7 String s7 = "H";
 8 String s8 = "ello";
 9 String s9 = s7 + s8;
10           
11 System.out.println(s1 == s2);  // true
12 System.out.println(s1 == s3);  // true
13 System.out.println(s1 == s4);  // false
14 System.out.println(s1 == s9);  // false
15 System.out.println(s4 == s5);  // false
16 System.out.println(s1 == s6);  // true

在虛擬機接收到指令,首先會從classPath 中加載這個MyTest.class ,讀取這個文件的二進制,然後把Appmain類的類信息存放到運行時數據區的方法區中。這一過程稱爲MyTest類的加載過程。

在解釋上面的以前我們來回來一個經常的面試題

String a = "str"  和  String b = new String(“str”)  ,   a == b ?

上面的答案是   flase ,爲什麼呢?  

== 比較的是棧中的引用地址

1.String a = "str" ------------         首先虛擬機會從方法區中去尋找是否有,有的話就直接把地址賦值給a ,沒有的話,就在方法區中開闢一個內存在存儲.(方法區的回收不會那麼頻繁,只有當內存不夠的時候纔會銷燬不用的常量,非靜態的)

2.String b = new String("str)  他是直接在堆內存中,直接開闢一個內存來存儲的,把地址給 b

所以  a == b = false

看過上面的現在講解下

s1 , s2 因爲是在加載的時候,內容都一樣所以肯定相等

s1 == s3這個地方有個坑,s3雖然是動態拼接出來的字符串,但是所有參與拼接的部分都是已知的字面量,在編譯期間,這種拼接會被優化,編譯器直接幫你拼好,因此String s3 = "Hel" + "lo";在class文件中被優化成String s3 = "Hello",所以s1 == s3成立。只有使用引號包含文本的方式創建的String對象之間使用“+”連接產生的新對象纔會被加入字符串池中

  s1 == s4當然不相等,s4雖然也是拼接出來的,但new String("lo")這部分不是已知字面量,是一個不可預料的部分,編譯器不會優化,必須等到運行時纔可以確定結果,結合字符串不變定理,鬼知道s4被分配到哪去了,所以地址肯定不同。對於所有包含new方式新建對象(包括null)的“+”連接表達式,它所產生的新對象都不會被加入字符串池中。

配上一張簡圖理清思路:

java字符串不變     

s1 == s9也不相等,道理差不多,雖然s7、s8在賦值的時候使用的字符串字面量,但是拼接成s9的時候,s7、s8作爲兩個變量,都是不可預料的,編譯器畢竟是編譯器,不可能當解釋器用,不能在編譯期被確定,所以不做優化,只能等到運行時,在堆中創建s7、s8拼接成的新字符串,在堆中地址不確定,不可能與方法區常量池中的s1地址相同。
jvm常量池,堆,棧內存分佈

     s4 == s5已經不用解釋了,絕對不相等,二者都在堆中,但地址不同。

     s1 == s6這兩個相等完全歸功於intern方法,s5在堆中,內容爲Hello ,intern方法會嘗試將Hello字符串添加到常量池中,並返回其在常量池中的地址,因爲常量池中已經有了Hello字符串,所以intern方法直接返回地址;而s1在編譯期就已經指向常量池了,因此s1和s6指向同一地址,相等。

 

總結:

在理解方法區的時候,我們要從加載類的理解去理解區分於運行時

 

借鑑的文章:

https://www.cnblogs.com/syp172654682/p/8082625.html

https://blog.csdn.net/qq_41675686/article/details/80400775

 

 

 

 

 

 

 

 

 

 

 

 

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