JDK1.7相對于JDK1.6,主要的變化就是將永久代中的字符串常量池移到堆內存中,交由堆管理。我們知道堆是JVM內存管理的主要區域,那么將字符串常量池放到堆內存中更方便高效的對字符串常量進行管理和垃圾回收。
而JDK1.8相對于JDK1.7來說,主要區別有兩點,一是將虛擬機棧和本地方法棧合二為一了,二是移除永久代,增加了元數據區,元數據區使用本地內存,只受計算機內存大小的限制。而永久代使用的還是堆內存空間,受堆內存大小的限制。
記錄正在執行的虛擬機字節碼指令的地址。為了線程切換后能恢復到正確的位置,每個線程都需要一個獨立的程序計數器,各個線程之間互不影響,獨立存儲,這也就是所謂的“線程私有區域”。
描述方法執行的內存模型,每個方法在執行時都會創建一個棧幀,每個棧幀存放的是局部變量表,操作數棧,動態鏈接,方法出口等信息,方法被調用到執行完成對應的是一個棧幀從入棧到出棧的過程。是線程私有的。
為虛擬機使用到的Native方法服務。注在HotSpot虛擬機中直接就把本地方法棧和虛擬機棧合二為一了。
存儲已被虛擬機加載的類信息、常量、靜態變量、即時編譯器編譯后的代碼等數據。在jdk1.8中已經去除了永久代,改用只受計算機本地內存大小限制的元空間來實現方法區,元空間參數(-XX:MetaspaceSize=512M -XX:MaxMetaspaceSize=1024M)。
運行時常量池是方法區的一部分。Class文件中除了有類的版本、字段、方法、接口等描述信息外,還有一項信息是常量池,用于存放編譯器生成的各種字面量和符號引用,這部分內容將在類加載后進入方法區的運行時常量池中存放。
1. 由于PermGen內存經常會溢出,引發惱人的 java.lang.OutOfMemoryError: PermGen,因此 JVM 的開發者希望這一塊內存可以更靈活地被管理,不要再經常出現這樣的 OOM。
2. 移除 PermGen 可以促進 HotSpot JVM 與 JRockit VM 的融合,因為 JRockit 沒有永久代。
i++:剛開始學java時候,i++先使用i,然后再自加1,為什么是這樣呢?
底層步驟:
1.從局部變量表取出 i 并壓入操作數棧。(入棧)
2.然后對局部變量表中的i自增1,將操作棧棧頂值取出使用。(自加1,出棧)
3.最后使用操作數棧的棧頂值更新局部變量表,如此線程從操作棧讀到的是自增之前的值。(更新)
++i:剛開始學java時候,++i先自加1然后再使用i,為什么是這樣呢?
----------------------------------------------------------
底層步驟:
1.先對局部變量表的 i 自增 1。(自加1)
2.然后取出并壓入操作數棧。(入棧)
3.再將操作棧棧頂值取出使用。(出棧)
4.最后使用棧頂值更新局部變量表,線程從操作棧讀到的是自增之后的值。(更新)
--------------------------------------------
之前之所以說 i++ 不是原子操作,即使使用 volatile 修飾也不是線程安全,就是因為可能 i 被從局部變量表取出,壓入操作數棧,操作數棧中自增,使用棧頂值更新局部變量表(寄存器更新寫入內存),其中分為 3 步,volatile 保證可見性,保證每次從局部變量表讀取的都是最新的值,但可能這 3 步可能被另一個線程的 3 步打斷,產生數據互相覆蓋問題,從而導致 i 的值比預期的小。
如果線程請求的棧深度大于虛擬機所允許的深度,則拋出StackOverflowError。
堆內存存儲對象實例。我們只要不斷地創建對象。并保證gc roots到對象之間有可達路徑來避免垃圾回收機制清除這些對象。就會在對象數量到達最大。堆容量限制后,產生內存溢出異常。
public class Cat {
public static void main(String[] args) {
List list = new ArrayList();
while (true) {
list.add(new Cat());
}
}
}
1.棧是線程私有的,棧的生命周期和線程一樣,每個方法在執行的時候就會創建一個棧幀,它包含局部變量表、操作數棧、動態鏈接、方法出口等信息,局部變量表又包括基本數據類型和對象的引用;
2.當線程請求的棧深度超過了虛擬機允許的最大深度時,會拋出StackOverFlowError異常,方法遞歸調用肯可能會出現該問題;
3.調整參數-xss去調整jvm棧的大??;
在Java虛擬機中,對象是在Java堆中分配內存的,這是一個普遍的常識。但是,有一種特殊情況,那就是如果經過逃逸分析(Escape Analysis)后發現,一個對象并沒有逃逸出方法的話,那么就可能被優化成棧上分配。這樣就無需在堆上分配內存,也無須進行垃圾回收了。這也是最常見的堆外存儲技術。
1)當一個對象在方法中被定義后,對象只在方法內部使用,則認為沒有發生逃逸。
2)當一個對象在方法中被定義后,它被外部方法所引用,則認為發生逃逸。例如作為調用參數傳遞到其他地方中。
運行時數據區 | 是否存在 | 是否存在 |
---|---|---|
程序計數器 | 否 | 否 |
虛擬機棧 | 是 | 否 |
本地方法棧 | 是 | 否 |
方法區 | 是(OOM) | 是 |
堆 | 是 | 是 |