Java基本型別的記憶體分配在棧還是堆
我們都知道在Java裡面new出來的物件都是在堆上分配空間儲存的,但是針對基本型別卻有所區別,基本型別可以分配在棧上,也可以分配在堆上,這是為什麼?
在這之前,我們先看下Java的基本型別8種分別是:
byte =>8bit short => 16bit int => 32bit long =>64bit folat => 單精度32位 double => 雙精度64位 boolean => 注意oracle官網文件介紹,boolean代表1bit的資訊,但它本身的size卻不是精確的,依賴於jvm和os的實現,比較常見的說法是,boolean單獨使用的時候,在編譯時是使用int代替的,如果是boobean陣列,則是使用1byte代替 char => 16bit
注意字串內部是用char陣列實現的,所以屬於引用型別。
基本型別在成員變數和區域性(local)變數的時候其記憶體分配機制是不一樣的。
如果是成員變數,那麼不分基本型別和引用型別都是在java的堆記憶體裡面分配空間,而區域性變數的基本型別是在棧上分配的。棧屬於執行緒私有的空間,區域性變數的生命週期和作用域一般都很短,為了提高gc效率,所以沒必要放在堆裡面。
如下程式碼:
public class DemoTest { int y;// 分佈在堆上 public static void main(String[] args) { int x=1; //分配在棧上 String name=new String("cat");//資料在堆上,name變數的指標在棧上 String address="北京";//資料在常量池,屬於堆空間,指標在棧 Integer price=4;//包裝型別同樣是引用型別,編譯時會自動裝拆相,所以資料在堆上,指標在棧 } }
在java裡面通過new出來的物件都在堆上分配,這裡有兩種特殊情況,
(1)字串的字面量
字串的字面量,沒有new關鍵字,但卻是在堆上分配記憶體的,嚴格的說是在堆裡面的字串常量池裡面。
(2)基本型別的包裝類
同樣的道理,針對各個基本型別的包裝型別,如:Integer,Double,Long等,這些屬於引用型別,我們直接在區域性方法裡面使用包裝型別賦值,那麼資料真正的記憶體分配還是在堆記憶體裡面,這裡有個隱式的拆裝箱來自動完成轉換,資料的指標是在棧上,包裝型別的出現主要是為了基本型別能夠用在泛型的設計上和使用null值,而基本型別則擁有更好的計算效能,這一點我們也需要注意。
思考:
如果你熟悉java的記憶體結構的話就會知道,堆 是所有執行緒共享的記憶體區域,棧 是每個執行緒獨享的,如果你將一個例項變數放在棧內,那麼就不存在多個執行緒訪問同一個物件資源了,這顯然是不對的,所以例項變數要在堆上建立,也不是執行緒安全的。
但是對於區域性變數,是在棧上建立的,每一次方法呼叫建立一個幀,獨享一份記憶體區域,其他的執行緒是不會訪問到該執行緒的資源,在 棧上建立也會減輕GC的壓力,隨著該方法的結束,幀出棧,相對應的記憶體消除,這種區域性變數佔用的記憶體自然就消失了,因此區域性變數是執行緒安全的。