由于之前看的容易忘記,是以特記錄下來,以便學習總結與更好了解,該系列博文也是第一次記錄,所有有好多不完善之處請見諒與留言指出,如果有幸大家看到該博文,希望報以參考目的看浏覽,如有錯誤之處,謝謝大家指出與留言。
目錄:
JVM的概念
JVM發展曆史
JVM種類
Java語言規範
JVM規範
一、初識JVM – JVM概念
1、JVM是Java Virtual Machine的簡稱。意為Java虛拟機
2、虛拟機:指通過軟體模拟的具有完整硬體系統功能的、運作在一個完全隔離環境中的完整計算機系統
3、有哪些虛拟機
-VMWare
-Visual Box
-JVM
4、不同虛拟機比較
VMWare或者Visual Box與JVM不同之處他們都是使用軟體模拟實體CPU的指令集,所模拟的對象都是真實存在的計算機,所模拟的cpu都是現實可找到的CPU案例,包括CPU,硬碟,記憶體等,JVM模拟的對象是現實找不到的,并沒有真實計算機去運作執行位元組碼,單純從軟體去做的設計。在正常的CPU中,都有若幹個寄存器,而jvm的cpu中除了PC寄存器外,其他的JVM本身對其他寄存器做了裁剪,因為他是純粹軟體去實作的,是以寄存器效果并沒有多大作用,引入而且會對JVM設計有運作帶來大量的麻煩,是以就去掉了。
5、JVM使用軟體模拟Java 位元組碼的指令集,與平時說的CPU指令集并不一樣
二、初識JVM —— Java和JVM的曆史
1、1996年 SUN JDK 1.0 Classic VM
— 純解釋運作,使用外挂進行JIT (這個性能是非常低的,是以開始讓人們對java感覺性能非常低)
2、1997年 JDK1.1 釋出
— AWT、内部類、JDBC、RMI、反射
3、1998年 JDK1.2 Solaris Exact VM(這個虛拟機壽命比較短,由于hsotspot的加入導緻他淘汰,但JDK1.2開始 稱為Java 2;J2SE J2EE J2ME 的出現加入Swing Collections)
(1)、JIT 解釋器混合
(2)、Accurate Memory Management 雖然淘汰但他卻做到精确記憶體管理,資料類型敏感
(3)、提升的GC性能
4、2000年 JDK 1.3 Hotspot 作為預設虛拟機釋出 (也加入(關與聲音一些API)JavaSound)
5、2002年 JDK 1.4 (最早的虛拟機)Classic VM退出曆史舞台 同時JDK1.4也加入Assert 正規表達式 NIO IPV6 日志API 加密類庫 這時候java類庫就相當完整了。
6、2004年釋出 JDK1.5 即 JDK5 、J2SE 5 、Java 5 (這個版本對java至關重要的)
加入了如下内容:
— 泛型 — 注解 — 裝箱 — 枚舉 — 可變長的參數 — Foreach循環
7、JDK1.6 JDK6加入如下内容:
— 腳本語言支援 — JDBC 4.0 — Java編譯器 API
8、2011年 JDK7釋出(由于在版本規劃中加入太多東西,一度難以釋出,是以好多原本計劃的内容推到jdk1.8中)
— 延誤項目推出到JDK8 — G1 — 動态語言增強 — 64位系統中的壓縮指針 — NIO 2.0
9、2014年 JDK8釋出
— Lambda表達式 — 文法增強 Java類型注解
10、2016年JDK9
— 子產品化
三、初識JVM—Java和JVM的曆史 —— 發展中的大事件
1、使用最為廣泛的JVM為HotSpot
2、HotSpot 為Longview Technologies開發 被SUN收購
3、2006年 Java開源 并建立OpenJDK
— HotSpot 成為Sun JDK和OpenJDK中所帶的虛拟機
4、2008 年 Oracle收購BEA
— 得到JRockit VM
5、2010年Oracle 收購 Sun
— 得到Hotspot 從此,Oracle擁有了兩個主流的虛拟機JRockit和Hotspot
6、Oracle宣布在JDK8時整合JRockit和Hotspot,優勢互補
— 在Hotspot基礎上,移植JRockit優秀特性 (在JDK1.8把他倆整合,因為Oracle不可能維護兩個虛拟機,是以把他們整合)
四、初識JVM —— 各式JVM
1、KVM
-- SUN釋出
-- IOS Android前,廣泛用于手機系統(以前java也是應用于手機)
2、CDC/CLDC HotSpot
-- 手機、電子書、PDA等裝置上建立統一的Java程式設計接口
-- J2ME的重要組成部分
3、JRockit
-- BEA
4、IBM 的J9 VM
-- 用于IBM内部程式,自己用。
5、Apache Harmony
-- 相容于JDK 1.5和JDK 1.6的Java程式運作平台
-- 與Oracle關系惡劣 阿帕奇退出JCP ,Java社群的分裂
-- OpenJDK出現後,受到挑戰 2011年 退役
-- 沒有大規模商用經曆
-- 對Android的發展有積極作用
五、初識JVM – 規範
(一)java規範
-- 文法
-- 變量
-- 類型
-- 文法
1、文法定義
-- If ThenStatement:
if ( Expression ) Statement
-- ArgumentList:支援多個參數清單
Argument
ArgumentList , Argument

2、詞法結構
-- \u + 4個16進制數字 表示UTF-16
-- 行終結符: CR, or LF, or CR LF.
-- 空白符
-- 空格 tab \t 換頁 \f 行終結符
-- 注釋
-- 辨別符
-- 關鍵字
辨別符:辨別字元但不是關鍵字或布爾值文字或零文字
IdentifierChars: JavaLetter IdentifierChars JavaLetterOrDigit
JavaLetter: any Unicode character that is a Java letter (see below)
JavaLetterOrDigit: any Unicode character that is a Java letter-or-digit (see below)
例子:
public static void 列印(){
System.out.println("中文方法哦");
}
public static void main(String[] args) {
列印();
}
— Int
0 2 0372 0xDada_Cafe 1996 0x00_FF__00_FF
— Long
0l 0777L 0x100000000L 2_147_483_648L 0xC0B0L
— Float
1e1f 2.f .3f 0f 3.14f 6.022137e+23f
— Double
1e1 2. .3 0.0 3.14 1e-9d 1e137
— 操作
+= -= *= /= &= |= ^= %= <<= >>= >>>=
4、類型和變量
— 元類型
byte short int long float char
— 變量初始值
boolean false
char \u0000
— 泛型
舉例:
class Value { int val; }
class Test {
public static void main(String[] args) {
int i1 = 3;
int i2 = i1;
i2 = 4;
System.out.print("i1==" + i1);
System.out.println(" but i2==" + i2);
Value v1 = new Value();
v1.val = 5;
Value v2 = v1;
v2.val = 6;
System.out.print("v1.val==" + v1.val);
System.out.println(" and v2.val==" + v2.val);
}
}
結果:i1==3 but i2==4
v1.val==6 and v2.val==6
i1 i2為不同的變量
v1 v2為引用同一個執行個體
5、還有其他一些規範
— Java記憶體模型
— 類加載連結的過程
— public static final abstract的定義
— 異常
— 數組的使用
— …….
Java語言規範定義了什麼是Java語言,則
JVM規範定義了什麼是JVM,是以java語言與jvm是相對獨立的,其他語言也可以運作在JVM中,隻要準守jvm規範
(二)JVM規範
-- Class檔案類型
-- 運作時資料
-- 幀棧
-- 虛拟機的啟動
-- 虛拟機的指令集
1、Java語言和JVM相對獨立。其他語言也可以運作在JVM比如:
(1)、 Groovy (2)、 Clojure (3)、Scala
2、JVM主要定義二進制class檔案和JVM指令集等
3、Class 檔案格式
4、數字的内部表示和存儲
— Byte -128 to 127 (-27 to 27 - 1)
5、returnAddress 資料類型定義
— 指向操作碼的指針。不對應Java資料類型,不能在運作時修改。Finally實作需要/
6、定義PC
7、堆
8、棧
9、方法區
10、JVM中整數的表達
— 原碼:第一位為符号位(0為正數,1為負數)
— 反碼:符号位不動,原碼取反
— 負數補碼:符号位不動,反碼加1
— 正數補碼:和原碼相同
— 列印整數的二進制表示
int a=-6;
for(int i=0;i<32;i++){ //因為整數有32位,是以按位取時,做32次循環,每一次取一位
int t=(a & 0x80000000>>>i)>>>(31-i); //0x80000000最高位為1,a & 0x80000000 就是把a的第一位取出來,a做為無符号的右移>>>(31-i) 這個多位,就列印出這個數字的2進制表示。
System.out.print(t); }
為什麼要用補碼?好處(1)因為0無正負,難以表示,是以用補碼表示計算才是正确的,(2)補碼能夠很好的參與計算機的二進制計算。
(1)計算0的表示:
(2)舉例:
11、Float的表示與定義
— 支援 IEEE 754
> s eeeeeeee mmmmmmmmmmmmmmmmmmmmmmm
以單精度為例:
舉例:
> e全0 尾數附加位為0 否則尾數附加位為1 位數雖為23其實指24位,隐藏在指數當中,可以通過e推導。
> s*m*2^(e-127)
12、一些特殊的方法
<clinit> 類的初始化方法
<init> 執行個體測初始化方法
在虛拟機中構造,比如類如果我們沒有初始化構造函數,他會預設幫那個我們初始化
13、JVM指令集
— 類型轉化
l2i
— 出棧入棧操作
aload astore
— 運算
iadd isub
— 流程控制
ifeq ifne
— 函數調用
invokevirtual invokeinterface invokespecial invokestatic
14、JVM需要對Java (庫)Library 提供以下支援:
— 反射 java.lang.reflect
— ClassLoader 類裝載器。java中是有加載器,但最終啟動是這個,在虛拟機中,而且java是無法操作它的。
— 初始化class和interface
— 安全相關 java.security
— 多線程
— 弱引用
15、JVM的編譯
— 源碼到JVM指令的對應格式
— Javap
— JVM反彙編的格式
<index> <opcode> [ <operand1> [ <operand2>... ]] [<comment>]
舉例:
void spin() {
int i;
for (i = 0; i < 100; i++) { ;
// Loop body is empty
}
}
虛拟機中直接執行的事下面這些指令代碼:
0 iconst_0 // Push int constant 0 首先操作0,是以把零入棧
1 istore_1 // Store into local variable 1 (i=0) 存儲局部變量1
2 goto 8 // First time through don't increment 第一次通過不增加
5 iinc 1 1 // Increment local variable 1 by 1 (i++) 增量局部變量1
8 iload_1 // Push local variable 1 (i) 推局部變量1入棧
9 bipush 100 // Push int constant 100 再把常量100入棧
11 if_icmplt 5 // Compare and loop if less than (i < 100) 比較和循環如果小于
14 return // Return void when done 完成時傳回
在例如:
public static int add(int a, int b) {
5.int c = ;
6. c = a + b;
7. return c;
8.}
檢視位元組碼的指令:javap -verbose ByteCode.class
add方法的位元組碼如下:
public static int add(int, int);
descriptor: (II)I //描述方法參數為兩個int類型的變量和方法的傳回類型是int的
flags: ACC_PUBLIC, ACC_STATIC //修飾方法public和static
Code:
stack=2, locals=3, args_size=2 //操作數棧深度為2,本地變量表容量為3,參數個數為2
0: iconst_0 //将int值0壓棧
1: istore_2 //将int值0出棧,存儲到第三個局部變量(slot)中
2: iload_0 //将局部變量表中第一個變量10壓棧
3: iload_1 //将局部變量表中第一個變量20壓棧
4: iadd //将操作數棧頂兩個int數彈出,相加後再壓入棧中
5: istore_2 //将棧頂的int數(30)彈出,存儲到第三個局部變量(slot)中
6: iload_2 //将局部變量表中第三個變量壓棧
7: ireturn //傳回棧中數字30
LineNumberTable:
line 5: 0 //代碼第5行對應位元組碼第0行
line 6: 2 //代碼第6行對應位元組碼第2行
line 7: 6 //代碼第7行對應位元組碼第6行
LocalVariableTable:
Start Length Slot Name Si
0 8 0 a I //a占用第1個solt
0 8 1 b I //b占用第2個solt
2 6 2 c I //c占用第3個solt
局部變量表:存放的一組變量的存儲空間。存放方法參數和方法内部定義的局部變量表。
在java編譯成class的時候,已經确定了局部變量表所需配置設定的最大容量。
局部變量表的最小機關是一個Slot。
虛拟機規範沒有明确規定一個Slot占多少大小。隻是規定,它可以放下boolean,byte,...reference &return address.
reference 是指一個對象執行個體的引用。關于reference的大小,目前沒有明确的指定大小。但是我們可以了解為它就是類似C++中的指針。
局部變量表的讀取方式是索引,從0開始。是以局部變量表可以簡單了解為就是一個表.
局部變量表的配置設定順序如下:
this 引用。可以認為是隐式參數。
方法的參數表。
根據局部變量順序,配置設定Solt。
一個變量一個solt,64為的占2個solt。java中明确64位的是long & double
為了盡可能的節約局部變量表,Solt可以重用。
注意:局部變量隻給予配置設定的記憶體,沒有class對象的準備階段,是以局部變量在使用前,必須先指派。
局部變量表可參考:https://www.tuicool.com/articles/URZrMnb
根據上面位元組碼畫出下面局部變量表和操作數棧之間的操作關系。
圖中調用add(10,20)傳入的參數是a=10;b=20。
-
指令0執行後:局部變量表中有兩個數字10、和20,操作數棧一個值0,程式計數器指向第0行位元組碼指令
0: iconst_0 //将int值0壓棧
-
指令1執行後:局部變量表中有三個數字10、20和0,操作數棧沒有值,程式計數器指向第1行位元組碼指令
1: istore_2 //将int值0出棧,存儲到第三個局部變量(slot)中
-
指令2執行後:局部變量表中有三個數字10、20和0,操作數棧一個值10,程式計數器指向第2行位元組碼指令
2: iload_0 //将局部變量表中第一個變量10壓棧
-
指令3執行後:局部變量表中有三個數字10、20和0,操作數棧兩個值10和20,程式計數器指向第3行位元組碼指令
3: iload_1 //将局部變量表中第一個變量20壓棧
-
指令4執行後:局部變量表中有三個數字10、20和0,操作數棧一個值30,程式計數器指向第4行位元組碼指令
4: iadd //将操作數棧頂兩個int數彈出10和20,相加後再壓入棧中
-
指令5執行後:局部變量表中有三個數字10、20和30,操作數棧沒有值,程式計數器指向第5行位元組碼指令
5: istore_2 //将棧頂的int數(30)彈出,存儲到第三個局部變量(slot)中
-
指令6執行後:局部變量表中有三個數字10、20和30,操作數棧一個值30,程式計數器指向第6行位元組碼指令
6: iload_2 //将局部變量表中第三個變量壓棧
-
指令7執行後:将棧中的數字傳回給調用方法,并銷毀此棧幀
7: ireturn //傳回棧中數字30
簡單總結介紹堆和棧:
堆是先進先出,而棧是先進後處
1. 棧(stack)與堆(heap)都是Java用來在Ram中存放資料的地方。與C++不同,Java自動管理棧和堆,
程式員不能直接地設定棧或堆。
2. 棧的優勢是,存取速度比堆要快,僅次于直接位于CPU中的寄存器。但缺點是,存在棧中的
資料大小與生存期必須是确定的,缺乏靈活性。另外,棧資料可以共享,詳見第3點。堆的優勢
是可以動态地配置設定記憶體大小,生存期也不必事先告訴編譯器,Java的垃圾收集器會自動收走這些
不再使用的資料。但缺點是,由于要在運作時動态配置設定記憶體,存取速度較慢
3. Java把記憶體劃分成兩種:一種是棧記憶體,一種是堆記憶體。在函數中定義的一些基本類型的變量
和對象的引用變量都在函數的棧記憶體中配置設定。當在一段代碼塊定義一個變量時,Java就在棧中為
這個變量配置設定記憶體空間,當超過變量的作用域後,Java會自動釋放掉為該變量所配置設定的記憶體空間,
該記憶體空間可以立即被另作他用。堆記憶體用來存放由new建立的對象和數組。在堆中配置設定的記憶體,
由Java虛拟機的自動垃圾回收器來管理。在堆中産生了一個數組或對象後,還可以在棧中定義一
個特殊的變量,讓棧中這個變量的取值等于數組或對象在堆記憶體中的首位址,棧中的這個變量就
成了數組或對象的引用變量。引用變量就相當于是為數組或對象起的一個名稱,以後就可以在程
序中使用棧中的引用變量來通路堆中的數組或對象
heap堆:用來存放new出來的東西
stack棧:局部變量。
data segment:靜态變量,字元串常量。
code segment:存放代碼
堆棧詳細可參考:https://blog.csdn.net/qq_24531461/article/details/70224449
此篇隻是介紹認識JVM以及規範,後續再單獨繼續深入。