概述
先Copy一個概念:JVM是基于堆棧的虛擬機。JVM為每個新創建的線程都分配一個堆棧.也就是說,對于一個Java程序來說,它的運行就是通過對堆棧的操作來完成的。堆棧以幀為單位保存線程的狀態。JVM對堆棧只進行兩種操作:以幀為單位的壓棧和出棧操作。
例子分析
堆是用來存儲new出來的對象,也就是真實對象的存儲地方。棧是用來存儲基本變量、局部變量及對象的引用的。
舉個來說明吧
class User {
private int id;
private String name;
public User(int sid, String sname) {
this.id = sid;
this.name = sname;
}
}
public void test() {
int a = 10;
int b = 10;
b = 12;
String c = "hello";
String d = new String("hello");
User user = new User(10, "hello");
}
基本變量類型都是存儲在棧中的,所以int型的a、b、c都存在棧中。具體的操作再來分析下:
int a = 10會檢查棧中有沒有10這個值,沒有就創建了一個10的值,再創建了a去指向10。
int b = 10同樣會檢查棧中有沒有10這個值,發現已經存在,則創建b指向已存在的10。在棧中,變量值是可以共享的。
b = 12檢查棧中有沒有12這個值,沒有就創建一個12的值,再把b的指向12,這樣也不會影響a的值。
說到String時,先說說JVM有一個常量存儲區域,是用來存常量的,像代碼定義直接雙引號中的內容就是存在常量存儲區域的。String不是基本變量類型。
String c = "hello"會先到常量存儲區域找hello,如果沒有,就在常量存儲區域創建hello值,棧中創建一個c指向常量區域的hello。
String d = new String("hello")是new一String對象,會先到常量區域檢查有沒hello值,如果有,就在堆中生成hello的一個對象副本,如果沒有就會在常量區先創建hello,再在堆中生成一個副本。在棧中生成d,指向堆中的hello對象,d是對象的引用。
User user = new User(10, "hello"),會構造函數,構造函數里的sid,sname是局部變量,會存儲在棧中,指向的值是棧中的10和常量存儲中的hello。在堆中會有new User這個對象,在棧中會有user這個對象的引用。這里要說一下,sid,sname是構造方法執行完后,會自動從棧中移除,而成員變量id,name是對象的屬性,會在堆中存在,需要等JAVA自動回收機制進行回收。
當執行完test方法后,棧中的基礎變量和值及對象引用都會被自動移除,釋放內存。
通過例子應該可以比較好理解哪些數據存在堆中,哪些數據存在棧中了。
總結
堆分配內存時,大小和生存期都是不確定的,要生成的時候才知道,遵循先進先出的原則,讀取速度比較慢。數據不能共享,每new一次都是一個新的對象。要由JAVA的垃圾回收機制來回收,所以容易造成內存多高。
棧分配內存時數據的大小和生存期是確定的,在編譯時就知道了,遵循先進后出的原則,讀取速度快,接近寄存器了。數據可以共享,節省內存,也比較快。在生存期結束后會自動釋放,效率更高。