一、java內存模型和java運行時數據區域的關係:主內存對應着java堆,工做內存對應着java棧。java
二、volatile
關鍵字,使得變量的更新在各個工做內存中都是實時可見的。在DCL的單例模式中有運用到!面試
由於jvm的運行時數據區域一直在改善,因此不一樣jdk版本之間會有不一樣。算法
一、程序計數器的做用,由於.java文件被編譯成.class文件,它做爲當前線程所執行的字節碼的行號指示器。當字節碼解釋器工做時,就是經過改變這個計算器的值來選取下一條要執行的字節碼指令。每條線程都有一個獨立的程序計數器。數組
二、本地方法棧就是執行本地native方法的棧,native方法由虛擬機實現!jvm
三、java虛擬機棧描述的是該線程執行java方法(method)時的內存模型。每個方法都對應一個棧幀,棧幀中的局部變量表存儲了方法中的基本數據類型變量、對象引用變量。函數
如上圖所示,局部變量表保存了方法中聲明的8種基本類型變量和對象引用變量。每個棧幀中還有一個指向運行時常量池的引用,這是指String類型。 下面有一個經典的String對象生成的面試題!
四、java堆是JVM中內存最大的一塊,被全部線程共享。幾乎全部的對象實例都在這裏分配,因此java堆也是JVM垃圾回收的主要區域。java堆又被分紅了年輕代,老年代;年輕代進一步能夠劃分爲Eden空間,From Survivor空間、To Survivor空間。性能
當咱們使用new關鍵字分配對象時,就是在java堆中生成對象。
下面分析一下對象生成時的狀況。測試
年輕代使用的是複製算法:每次Minor GC把Eden區和一塊Survivor區的存活對象複製到另外一塊Survivor區。老年代使用的是標記-整理算法:每次Major GC把存活對象都想內存空間的一端移動,而後直接清理掉端邊界之外的內存。spa
大對象如數組、很長的字符串,直接進入老年代空間。
五、方法區用於存儲JVM加載的類信息、final常量、static靜態變量等數據,方法區中的數據都是整個程序中惟一的。方法區還包含了運行時常量池,主要存放編譯期生成的字面量和符號引用(在類加載後放入)。String對象的字面量就會被放入到運行時常量池中。操作系統
垃圾回收在方法區主要是對常量的回收和對類型的卸載。
參考文章:java8的jvm內存區域。
元空間和永久代的性質是同樣的,都是對JVM方法區的實現,做用是同樣的。不過元空間與永久代之間最大的區別在於:元空間並不在虛擬機JVM內存中,而是使用本地內存。
爲何用元空間取代永久代呢?
JDK1.4以後加入的NIO,引入了基於通道channel和緩衝區buffer的IO,直接使用native函數分配堆外內存,顯著提升IO性能,避免了原來BIO的在java堆和naive堆中來回複製數據。
參考文章:字符串常量池。
下面來分析一下咱們常見的生成對象或基本數據類型變量的內存模型。這樣能夠對JVM有一個更好的理解。
int i =3;
,一個方法對應一個棧幀,方法中的基本數據類型變量直接在棧幀中分配。若是是static、final類型的基本數據類型則存儲在運行時常量池中,和String同樣。Object o1 = new Object();
,對象引用(Object o1
)存儲在棧幀中,可是對象數據(new Object()
)存儲在java堆中,對象類型數據(Class等信息)存儲在方法區中。String s1 = new String("abcd");
,使用new聲明的對象,對象引用(String s1
)存儲在棧幀中,對象數據(new String(「abcd」)
)存儲在java堆中,字符串值(「abcd」
)存儲在運行時常量池中。String s2 = 「abc」
,對象引用(String s2
)存儲在棧幀中,字符串值(「abc」
)存儲在運行時常量池中。java棧、java堆、方法區這3者之間的關係大概就是上面的分析所示。
OutOfMemoryError
若是java堆中分配的對象太多,且GC後內存空間仍是不夠用。下面經過循環生成對象來消耗內存空間進行測試。
相關指令:VM Args: -Xms20m -Xmx40m
,表示JVM分配的堆內存最小爲20MB,最大爲40MB。
public static void main(String[] args) { while (true) { List<Object> list = new ArrayList<>(10); list.add(new Object()); } }
StackoverflowError
若是java棧的棧深度大於JVM容許的深度,就會拋出該錯誤。下面經過無限遞歸調用來進行堆棧進行測試。
相關指令:VM Args: -Xss128k
,表示JVM分配的棧容量爲128KB。
public class StackOOM { private int length = 1; public void stackLeak() { length++; stackLeak(); } public static void main(String[] args) { StackOOM stackOOM = new StackOOM(); stackOOM.stackLeak(); } }