定義: 模擬某種計算機體系結構,執行特定指令集的軟件
分類 系統虛擬機(VMware,Virtual Box等), 進程虛擬機java
特色:並不會完整的模擬一個操做系統的運行環境,僅僅提供了特定指令集的運行環境
實例: JVM, Adobe Flash, FC模擬器算法
特色:把特定指令集範圍進一步限定爲高級語言。
屬於進程虛擬機的一種,若有 JVM, .NET CLR, P-Code緩存
可執行java語言的高級語言虛擬機。java語言虛擬機並不必定能夠稱爲JVM, 例如:Apache Harmony數據結構
共有設計,私有實現
java虛擬機規範聲明瞭概念模型,這些概念模型不約束虛擬機的具體實現,只要求虛擬機實現的效果在外部看起來和規範描述同樣便可。
例如規範中規定了java堆中的對象所在的內存,須要虛擬機自動完成垃圾回收,這個實現過程能夠不一樣,可是效果須要相同。app
其中 :
方法區和堆是全部線程共享數據區。
虛擬機棧、本地方法棧、程序計數器 是線程私有的。jvm
在編譯程序代碼時,棧幀須要的局部變量表的大小和操做數棧的深度,在編譯期間就已經徹底肯定了,java虛擬機會把這些數據徹底寫在class文件的code表中。所以一個棧幀須要分配的內存大小不會受程序運行期間變量數據的影響,而僅僅取決於具體的java虛擬機。
在一個線程裏面方法的調用鏈可能很長,不少方法可能都同時處於執行的狀態,對於執行引擎而言,在活動線程之中,只有位於java虛擬機棧頂的棧幀纔是有效的,這個棧幀被稱爲當前棧幀,與此棧幀相關聯的方法被稱爲當前方法。虛擬機中全部執行的字節碼指令都針對當前方法和當前棧幀操做。ide
定義: 是一組變量值的存儲空間,用於存儲方法,參數和方法內部定義的局部變量等等。
在java編譯器編譯class文件時,就已經肯定了該方法須要的局部變量表的最大容量,局部變量表是以槽(Slot)爲最小單位的。java虛擬機規範中沒有明確一個slot的具體佔用的內存大小,只是描述了任何slot能夠儲存的類型。性能
在cmd中輸入測試
copy con Test.java
而後輸入程序:ui
public class Test{ public int calc(){ int a =100; int b =200; int c =300; return (a+b)*c; } }
而後編譯
javac Test.java
再查看字節碼:
javap -verbose Test.class
獲得:
0: bipush 100 //把100入棧到操做數棧的棧頂 2: istore_1 //把操做數棧頂的元素出棧並把此元素存儲在局部變量表中的1號Slot 3: sipush 200 6: istore_2 7: sipush 300 10: istore_3 11: iload_1 //將局變量表中的爲1號的操做數入棧到操做數棧棧頂 12: iload_2 //將局變量表中的爲2號的操做數入棧到操做數棧棧頂 13: iadd //將操做數棧棧頂的兩個元素出棧,而後相加入棧 14: iload_3 15: imul 16: ireturn
注意: 在 idea中能夠經過設置選項中的run --> Edit configuration --> configuration --> VM options 設置java虛擬機棧的大小 添加: -Xss128k
便可。
StackOverflowError異常
public class JavaVMStackSOF { private int stackLength = 1; public void stackLeak(){ stackLength++; stackLeak(); } public static void main(String[] args) { JavaVMStackSOF oom = new JavaVMStackSOF(); try{ oom.stackLeak(); }catch (Throwable e){ System.out.println("length" + oom.stackLength); throw e; } } }
OutOfMemoryError異常
public class JavaVMStackOOM { private void dontStop(){ while(true){ } } public void stackLeakThread(){ while(true){ Thread thread = new Thread(new Runnable() { @Override public void run() { dontStop(); } }); thread.start(); } } public static void main(String[] args) { JavaVMStackOOM oom = new JavaVMStackOOM(); oom.stackLeakThread(); } }
特徵:
劃分方式:
從棧到堆的關聯過程:
第二種實現方式:
對比:
第二種方式中,reference中存儲的是穩定的句柄的地址,在對象被移動時(對象常常被移動,垃圾回收時會發生移動),只會改變句柄池中的到對象類型數據的指針,而reference不會改變。
第一種方式,速度更快,對比使用句柄的方式,節省了指針開銷,reference直接指向了對象的實例數據。
可能發生的異常:
出現OOM實例以下:
不斷的建立對象,而且保證這些內存不被回收便可作到。
public class javaHeapOOM { static class OOMObject{ } public static void main(String[] args) { List<OOMObject> list = new ArrayList<>(); while(true){ list.add(new OOMObject()); } } }
注意:
類實例數據 和 類類型信息
實例數據是指在類中定義的各類實例對象以及它們的值,而類型信息是指定義在類代碼中的常量,靜態變量,類中聲明的各類方法,字段等,還會包括即時編譯器編譯產生的數據。
永久代與方法區
永久代變遷過程致使的異常差別:
實例1:
public class RuntimeConstantPoolChange { public static void main(String[] args) { String str1 = new StringBuilder("hptj").append("zzj").toString(); System.out.println(str1.intern() == str1); String str2 = new StringBuilder("ja").append("va").toString(); System.out.println(str2.intern() == str2); } }
intern方法:按期的維護了一個字符串池,若是字符串中有這個字符串,則會返回這個池中常量的地址,若是這個字符串中沒有這個字符串所需的常量,則jvm會先把這個字符串加到字符串常量池中,而後再返回這個字符串的地址。
可知:java1.7開始後字符串常量池被移到了Heap中,
當採用jdk1.6運行時,則出現 false false ,由於 str1.intern返回的是常量池中的地址,而str1本生的地址是在heap中的是不可能相同的。
當採用jdk1.7運行時,則出現 true false , 由於 str1.intern和heap都是堆中的地址,則這個hptjzzj字符串在heap中沒有出現過,則會加入到heap中的常量池中,並返回此地址和str1在堆中的地址一致,而java字符串在堆常量池中已經存在,當new以後會新建一個對象,因此是不相等的。