通常我們定義一個基本數據類型的變量,一個對象的引用,還有就是函數調用的
現場保存都使用 JVM 中的棧空間;而通過 new 關鍵字和構造器創建的對象則放在
堆空間,堆是垃圾收集器管理的主要區域,由於現在的垃圾收集器都采用分代收
集算法,所以堆空間還可以細分為新生代和老生代,再具體一點可以分為 Eden、
Survivor(又可分為 From Survivor 和 To Survivor)、Tenured;方法區和堆都
是各個線程共享的內存區域,用於存儲已經被 JVM 加載的類信息、常量、靜態變
量、JIT 編譯器編譯后的代碼等數據;程序中的字面量(literal)如直接書寫的 100、”
hello”和常量都是放在常量池中,常量池是方法區的一部分,。棧空間操作起來
最快但是棧很小,通常大量的對象都是放在堆空間,棧和堆的大小都可以通過 JVM
的啟動參數來進行調整,棧空間用光了會引發 StackOverflowError,而堆和常量
池空間不足則會引發 OutOfMemoryError。
String str = new String("hello");
上面的語句中變量 str 放在棧上,用 new 創建出來的字符串對象放在堆上,而”
hello”這個字面量是放在方法區的。
補充 1:較新版本的 Java(從 Java 6 的某個更新開始)中,由於 JIT 編譯器的發
展和”逃逸分析”技術的逐漸成熟,棧上分配、標量替換等優化技術使得對象一
定分配在堆上這件事情已經變得不那么絕對了。
補充 2:運行時常量池相當於 Class 文件常量池具有動態性,Java 語言並不要求
常量一定只有編譯期間才能產生,運行期間也可以將新的常量放入池中,String
類的 intern()方法就是這樣的。
看看下面代碼的執行結果是什么並且比較一下 Java 7 以前和以后的運行結果是否
一致。
String s1 = new StringBuilder("go")
.append("od").toString();
System.out.println(s1.intern() == s1);
String s2 = new StringBuilder("ja")
.append("va").toString();
System.out.println(s2.intern() == s2);