*.java文件-->編譯器-->*.class文件-->線程啓動(main)-->jvm-->操做系統-->硬件java
經過上面的流程咱們能夠看出java程序的執行順序,那麼jvm究竟是什麼,class文件究竟是如何在jvm中運行就顯得很重要了。linux
openjdk源碼地址http://hg.openjdk.java.net/jdk9算法
JVM是一個計算機模型,JVM對Java可執行代碼,即字節碼(Bytecode)的格式給出了明確的規格。這一規格包括操做碼和操做數的語法(也就是cpu指令集)和數值、標識符的數值表示方式、以及Java類文件中的Java對象、常量緩衝池在JVM的存儲映象。windows
JVM指令系統、JVM寄存器、JVM 棧結構、JVM 碎片回收堆、JVM 存儲區架構
Java指令也是由操做碼和操做數兩部分組成,與RISC CPU採用的編碼方式是一致的,也就是精簡指令集,目前UNIX、Linux、MacOS系統使用RISC,咱們目前知道的x86架構CPU使用的CISC編碼,也就是複雜指令集。app
1.pc程序計數器框架
2.optop操做數棧頂指針dom
3.frame當前執行環境指針jvm
windows操做系統裝入JVM是經過jdk中Java.exe來完成,經過下面4步來完成JVM環境。
1.建立JVM裝載環境和配置
2.裝載JVM.dll(C:\Program Files\Java\jre1.8.0_151\bin\server linux在jre/lib/server下)
3.初始化JVM.dll並掛接到JNIENV(JNI調用接口)實例
4.調用JNIEnv實例裝載並處理class類。
JVM虛擬機至關於x86計算機系統,Java解釋器至關於x86CPU
JVM定義了若干個程序執行期間使用的數據區域。這個區域裏的一些數據在JVM啓動的時候建立,在JVM退出的時候銷燬。而其餘的數據依賴於每個線程,在線程建立時建立,在線程退出時銷燬。分別有程序計數器,堆,棧,方法區,運行時常量池
局部變量:對應vars寄存器指向該變量表中的第一個局部變量,用於存儲一個類的方法中所用到的局部變量。
執行環境:對應frame寄存器的當前執行環境指針,用於保存解釋器對Java字節碼進行解釋過程當中所需的信息。它們是:上次調用的方法、局部變量指針和操做數棧的棧頂和棧底指針。執行環境是一個執行一個方法的控制中心。例如:若是解釋器要執行iadd(整數加法),首先要從frame寄存器中找到當前執行環境,然後便從執行環境中找到操做數棧,從棧頂彈出兩個整數進行加法運算,最後將結果壓入棧頂。
操做數棧:對應optop寄存器的操做數棧頂指針,操做數棧用於存儲運算所需操做數及運算的結果。
垃圾回收機制(GC)只發生在線程共享區,也就是堆和方法區,棧不須要回收,線程銷燬則棧也銷燬, 也就是上圖的heap space與method area會發生gc。
經過上圖能夠發現heap space被分爲兩部分:
因爲算法篇幅太長具體算法可自行查閱資料,主要介紹gc算法發生在什麼區。
分代蒐集算法:是由複製算法、標記/整理、標記/清除算法共同組成
複製算法發生在Young Generation
標記/整理和標記/清除算法發生在Old Generation和Permanent Generation
棧中通常存放的都是對象的指針和基本類型,存取速度比堆要快,僅次於直接位於CPU中的寄存器。但缺點是,存在棧中的數據大小與生存期必須是肯定的,缺少靈活性。
/** * Created by liustc on 2018/4/20. */ public class JvmTest { public static void main(String[] args) { int a=0; int b=0; System.out.print(a==b); } }
"C:\Program Files\Java\jdk1.8.0_151\bin\java" true Process finished with exit code 0
編譯器先處理int a = 0;首先它會在棧中建立一個變量爲a的引用,而後查找有沒有字面值爲0的地址,沒找到,就開闢一個存放0這個字面值的地址,而後將a指向0的地址。接着處理int b = 0;在建立完b的引用變量後,因爲在棧中已經有0這個字面值,便將b直接指向0的地址。這樣,就出現了a與b同時均指向0的狀況
/** * Created by liustc on 2018/4/20. */ public class JvmTest { public static void main(String[] args) { int a=0; int b=0; a=1; System.out.print("a="+a); System.out.print("b="+b); System.out.print(a==b); } }
"C:\Program Files\Java\jdk1.8.0_151\bin\java" a=1b=0false Process finished with exit code 0
再令a=1;那麼,b不會等於1,仍是等於0。在編譯器內部,遇到a=1;時,它就會從新搜索棧中是否有1的字面值,若是沒有,從新開闢地址存放1的值;若是已經有了,則直接將a指向這個地址。所以a值的改變不會影響到b的值。
/** * Created by liustc on 2018/4/20. */ public class JvmTest { public static void main(String[] args) { String str1 = "abc"; String str2 = "abc"; System.out.println(str1==str2); } }
"C:\Program Files\Java\jdk1.8.0_151\bin\java" true Process finished with exit code 0
/** * Created by liustc on 2018/4/20. */ public class JvmTest { public static void main(String[] args) { String str1 = "abc"; String str2 = "abc"; str1 = "bcd"; System.out.println(str1 + "," + str2); System.out.println(str1==str2); } }
"C:\Program Files\Java\jdk1.8.0_151\bin\java" bcd,abc false Process finished with exit code 0
賦值的變化致使了類對象引用的變化,str1指向了另一個新對象!而str2仍舊指向原來的對象。上例中,當咱們將str1的值改成"bcd"時,JVM發如今棧中沒有存放該值的地址,便開闢了這個地址,並建立了一個新的對象,其字符串的值指向這個地址。
/** * Created by liustc on 2018/4/20. */ public class JvmTest { public static void main(String[] args) { String str1 = "abc"; String str2 = "abc"; str1 = "bcd"; String str3 = str1; System.out.println(str3); String str4 = "bcd"; System.out.println(str1 == str4); } }
"C:\Program Files\Java\jdk1.8.0_151\bin\java" bcd true Process finished with exit code 0
str3這個對象的引用直接指向str1所指向的對象(注意,str3並無建立新對象)。當str1改完其值後,再建立一個String的引用 str4,並指向因str1修改值而建立的新的對象。能夠發現,這回str4也沒有建立新的對象,從而再次實現棧中數據的共享。
String類
/** * Created by liustc on 2018/4/20. */ public class JvmTest { public static void main(String[] args) { String str1 = new String("abc"); String str2 = "abc"; System.out.println(str1==str2); } }
"C:\Program Files\Java\jdk1.8.0_151\bin\java" false Process finished with exit code 0
以上代碼說明,只要是用new()來新建對象的,都會在堆中建立,並且其字符串是單獨存值的,即便與棧中的數據相同,也不會與棧中的數據共享。
使用String str = "abc";的方式,能夠在必定程度上提升程序的運行速度,由於JVM會自動根據棧中數據的實際狀況來決定是否有必要建立新對象。而對於String str = new String("abc");的代碼,則一律在堆中建立新對象,而無論其字符串值是否相等,是否有必要建立新對象,從而加劇了程序的負擔。這個思想應該是 享元模式的思想。
因爲String類的性質,當String變量須要常常變換其值時,應該考慮使用StringBuffer類,以提升程序效率。
執行時間上寄存器 < 堆棧 < 堆
/** * Created by liustc on 2018/4/20. */ public class JvmTest { public static void main(String[] args) { String s1 = "ja"; String s2 = "va"; String s3 = "java"; String s4 = s1 + s2; System.out.println(s3 == s4); System.out.println(s3.equals(s4)); } }
"C:\Program Files\Java\jdk1.8.0_151\bin\java" false true Process finished with exit code 0
是否是很矛盾啊!是否是又懵逼了?
打印false的緣由是,java 重載了「+」,查看java字節碼能夠發現「+」實際上是調用了StringBuilder 因此使用了「+」實際上是生成了一個新的對象。因此(s3 == s4)打印false
/** * Created by liustc on 2018/4/20. */ public class JvmTest { public static void main(String[] args){ long maxMemory = Runtime.getRuntime().maxMemory();//返回Java虛擬機試圖使用的最大內存量。 Long totalMemory = Runtime. getRuntime().totalMemory();//返回jvm實例佔用的內存。 System.out.println("MAX_MEMORY ="+maxMemory +"(字節)、"+(maxMemory/(double)1024/1024) + "MB"); System.out.println("TOTAL_ MEMORY = "+totalMemory +"(字節)"+(totalMemory/(double)1024/1024) + "MB"); } }
"C:\Program Files\Java\jdk1.8.0_151\bin\java" -XX:+PrintGCDetails MAX_MEMORY =1868038144(字節)、1781.5MB TOTAL_ MEMORY = 126877696(字節)121.0MB Heap PSYoungGen total 37888K, used 3932K [0x00000000d6400000, 0x00000000d8e00000, 0x0000000100000000) eden space 32768K, 12% used [0x00000000d6400000,0x00000000d67d7320,0x00000000d8400000) from space 5120K, 0% used [0x00000000d8900000,0x00000000d8900000,0x00000000d8e00000) to space 5120K, 0% used [0x00000000d8400000,0x00000000d8400000,0x00000000d8900000) ParOldGen total 86016K, used 0K [0x0000000082c00000, 0x0000000088000000, 0x00000000d6400000) object space 86016K, 0% used [0x0000000082c00000,0x0000000082c00000,0x0000000088000000) Metaspace used 3325K, capacity 4494K, committed 4864K, reserved 1056768K class space used 363K, capacity 386K, committed 512K, reserved 1048576K Process finished with exit code 0
將jvm堆初始值改小,觸發gc回收
import java.util.Random; /** * Created by liustc on 2018/4/20. */ public class JvmTest { public static void main(String[] args){ long maxMemory = Runtime.getRuntime().maxMemory();//返回jvm試圖使用的最大內存量。 Long totalMemory = Runtime. getRuntime().totalMemory();//返回jvm實例的內存大小。 System.out.println("MAX_MEMORY ="+maxMemory +"(字節)、"+(maxMemory/(double)1024/1024) + "MB"); System.out.println("TOTAL_ MEMORY = "+totalMemory +"(字節)"+(totalMemory/(double)1024/1024) + "MB"); String str = "www.baidu.com"; while(true){ str += str + new Random().nextInt(88888888) + new Random().nextInt(99999999); } }}
"C:\Program Files\Java\jdk1.8.0_151\bin\java" -XX:+PrintGCDetails MAX_MEMORY =1868038144(字節)、1781.5MB TOTAL_ MEMORY = 126877696(字節)121.0MB [GC (Allocation Failure) [PSYoungGen: 32247K->2729K(37888K)] 32247K->10124K(123904K), 0.0045031 secs] [Times: user=0.01 sys=0.03, real=0.00 secs] [GC (Allocation Failure) [PSYoungGen: 32912K->4469K(70656K)] 40307K->26638K(156672K), 0.0121112 secs] [Times: user=0.00 sys=0.00, real=0.01 secs] [GC (Allocation Failure) [PSYoungGen: 66160K->776K(70656K)] 88329K->59879K(156672K), 0.0141096 secs] [Times: user=0.03 sys=0.02, real=0.01 secs] [Full GC (Ergonomics) [PSYoungGen: 776K->0K(70656K)] [ParOldGen: 59103K->37630K(116224K)] 59879K->37630K(186880K), [Metaspace: 3408K->3408K(1056768K)], 0.0143902 secs] [Times: user=0.03 sys=0.00, real=0.01 secs] [Full GC (Ergonomics) [PSYoungGen: 60370K->0K(70656K)] [ParOldGen: 96726K->74565K(172032K)] 157096K->74565K(242688K), [Metaspace: 3409K->3409K(1056768K)], 0.0598124 secs] [Times: user=0.08 sys=0.00, real=0.06 secs] [GC (Allocation Failure) [PSYoungGen: 60382K->32K(95744K)] 1257771K->1226968K(1463808K), 0.0227293 secs] [Times: user=0.06 sys=0.01, real=0.02 secs] [GC (Allocation Failure) [PSYoungGen: 32K->32K(131584K)] 1226968K->1226968K(1499648K), 0.0037586 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] [Full GC (Allocation Failure) [PSYoungGen: 32K->0K(131584K)] [ParOldGen: 1226936K->355271K(483840K)] 1226968K->355271K(615424K), [Metaspace: 3409K->3409K(1056768K)], 0.1616835 secs] [Times: user=0.19 sys=0.09, real=0.16 secs] [GC (Allocation Failure) [PSYoungGen: 2499K->32K(158208K)] 1303306K->1300838K(1526272K), 0.0037952 secs] [Times: user=0.06 sys=0.00, real=0.00 secs] [GC (Allocation Failure) [PSYoungGen: 32K->32K(158208K)] 1300838K->1300838K(1526272K), 0.0036491 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] [Full GC (Allocation Failure) [PSYoungGen: 32K->0K(158208K)] [ParOldGen: 1300806K->473463K(622080K)] 1300838K->473463K(780288K), [Metaspace: 3409K->3409K(1056768K)], 0.1641897 secs] [Times: user=0.30 sys=0.06, real=0.16 secs] [GC (Allocation Failure) [PSYoungGen: 0K->0K(250880K)] 946230K->946230K(1618944K), 0.0027229 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] [GC (Allocation Failure) [PSYoungGen: 0K->0K(258560K)] 946230K->946230K(1626624K), 0.0027747 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] [Full GC (Allocation Failure) [PSYoungGen: 0K->0K(258560K)] [ParOldGen: 946230K->709846K(879104K)] 946230K->709846K(1137664K), [Metaspace: 3409K->3409K(1056768K)], 0.1013768 secs] [Times: user=0.28 sys=0.02, real=0.10 secs] [GC (Allocation Failure) [PSYoungGen: 0K->0K(353280K)] 709846K->709846K(1721344K), 0.0049384 secs] [Times: user=0.00 sys=0.00, real=0.01 secs] [Full GC (Allocation Failure) Exception in thread "main" java.lang.OutOfMemoryError: Java heap space [PSYoungGen: 0K->0K(353280K)] [ParOldGen: 709846K->709816K(900608K)] 709846K->709816K(1253888K), [Metaspace: 3409K->3409K(1056768K)], 0.1792920 secs] [Times: user=0.39 sys=0.00, real=0.18 secs] Heap at java.util.Arrays.copyOf(Arrays.java:3332) PSYoungGen total 353280K, used 14028K [0x00000000d6400000, 0x00000000ec700000, 0x0000000100000000) at java.lang.AbstractStringBuilder.ensureCapacityInternal(AbstractStringBuilder.java:124) eden space 352768K, 3% used [0x00000000d6400000,0x00000000d71b3070,0x00000000ebc80000) at java.lang.AbstractStringBuilder.append(AbstractStringBuilder.java:674) from space 512K, 0% used [0x00000000ec680000,0x00000000ec680000,0x00000000ec700000) at java.lang.StringBuilder.append(StringBuilder.java:208) to space 4608K, 0% used [0x00000000ebe00000,0x00000000ebe00000,0x00000000ec280000) at JvmTest.main(JvmTest.java:15) ParOldGen total 1368064K, used 709816K [0x0000000082c00000, 0x00000000d6400000, 0x00000000d6400000) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) object space 1368064K, 51% used [0x0000000082c00000,0x00000000ae12e1e8,0x00000000d6400000) Metaspace used 3440K, capacity 4494K, committed 4864K, reserved 1056768K at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) class space used 377K, capacity 386K, committed 512K, reserved 1048576K at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:498) at com.intellij.rt.execution.application.AppMain.main(AppMain.java:140) Process finished with exit code 1