虛擬機 1.1 發展歷程 1.1.1 java往事 Java誕生在一群懶惰、急躁而傲慢的程式天才之中。 1990年12月,Sun的工程師Patrick Naughton被當時糟糕的Sun C++工具折磨的快瘋了。他大聲抱怨,並威脅要離開Sun轉投當時在Steve Jobs領導之下的NeXT公 ...
虛擬機
1.1 發展歷程
1.1.1 java往事
Java誕生在一群懶惰、急躁而傲慢的程式天才之中。
1990年12月,Sun的工程師Patrick Naughton被當時糟糕的Sun C++工具折磨的快瘋了。他大聲抱怨,並威脅要離開Sun轉投當時在Steve Jobs領導之下的NeXT公司。領導層為了留住他,給他一個機會,啟動了一個叫做Stealth(秘密行動)的項目。
隨著James Gosling等人的加入,這個項目更名為Green。其目標是使用C++為嵌入式設備開發一種新的基礎平臺技術,James Gosling本人負責開發一個編輯器。正如人們事後分析的那樣,這位天才的程式員太懶惰,所以沒有把C++學好,開發中碰了一頭包。於是他決定開發一種新的編程語言。他把這種語言命名為C++++--,意思是C++ “加上一些好東西,減去一些壞東西”。顯然這個糟糕的名字不可能長久,於是很快這種頗受同伴喜愛的小語言被命名為Oak。
到了1992年9月,Oak語言連同Green OS和一些應用程式一起發佈在稱做Start 7的小設備上,有了第一次精彩的亮相。隨後,Sun開了一家名為FirstPerson的公司,整個團隊被轉移到這家公司里研發機頂盒,以投標時代華納公司的一個項目。這幫天才被技術狂熱所鼓舞,開發出了一個高交互性的設備,結果沒想到時代華納公司和有線電視服務商並不願意用戶擁有那麼大的控制權,從而在競標之戰中敗給了SGI。
Sun無奈地關閉了FirstPerson,召回了整個團隊,java的出路卻沒有因此而斷送,隨著互聯網發展的涌動,java開始離開嵌入式小設備,往互聯網傾斜。1994年,Oak被命名為Java,回到了激情澎湃的IT產業,抓住互聯網的大潮,從此一發不可收拾。
剩下的事情,大家都知道了……
1.1.2 版本迭代
-
1991 年,James Gosling 博士發佈產品 Oak( 橡樹),這是 Java 語言的前身。
-
1995 年,Oak 語言改名為 Java。
-
1996 年,JDK(Java開發所使用的工具包)1.0 發佈,提供了純解釋執行的 Java 虛擬機實現:Sun Classic VM。
-
1997 年,JDK1.1 發佈,代表技術有:JDBC、JavaBeans、內部類、反射。
-
1998 年,JDK1.2 發佈,Java 技術體系被拆分為 J2SE、J2EE、J2ME 三大體系。
- 2000 年,JDK1.3 發佈,預設的 Java 虛擬機由 Sun Classic VM 改為 HotSopt。
-
2002 年,JDK1.4 發佈,Java 真正走向成熟,代表技術有:正則表達式、NIO等。
-
2004 年,JDK5.0 發佈,對語法易用性做了很大改進,新增了泛型、枚舉等,代表技術有:併發包等。
-
2006 年,JDK6.0 發佈,將 J2EE/J2SE/J2ME 的命名方式改為 Java SE 6、Java EE 6、Java ME 6。
-
2009 年,Sun 公司因為經營不善被 Oracle 公司收購。
-
2011 年,JDK7 發佈。
-
2013 年,JDK8(LTS) 發佈,函數式編程,lamda表達式。
-
2017年,JDK9
-
2018年,JDK 10,11(LTS)正式發佈
-
2019年,JDK 12,13
-
2020年,JDK 14,15
-
2021年,JDK 16,17(LTS)
附:sun與微軟的軼事
java誕生的1995年,正是微軟在軟體產業地位達到巔峰的時代。但是這個初出茅廬的毛頭小子硬是引起了微軟帝國的關註。所以96年微軟就向sun申請了java認證。
微軟的加持確實推動了人們對java的信心和興趣。
但是好景不長,從1997年發佈Visual J++的第一個版本開始,微軟就開始在Java中摻入自己的私有擴展。這毫無疑問引起Sun的高度重視。
1997年10月,Sun向美國加州地方法院起訴微軟公司違反兩公司就微軟使用Java技術所簽定的合同,指控微軟公司在自己的Java產品中做了“不恰當的修改”,違反了合同中承諾向用戶提供Java相容產品的條款。
這一官司一直打到了2001年1月雙方達成和解。
到了2001年7月,微軟公佈新版的Windows XP將不再支持Sun的JVM,並且推出了.NET平臺與Java分庭抗禮。
當然目前.net用的人少了,這是後話。
1.1.3 兩種jdk
openjdk vs oraclejdk:
- Oracle JDK將更多地關註穩定性,它重視更多的企業級用戶,而OpenJDK經常發佈以支持其他特性,不太穩定。
- Oracle JDK支持長期發佈的更改(LTS),而Open JDK僅支持計劃和完成下一個發行版。
- Oracle JDK根據二進位代碼許可協議獲得許可,而OpenJDK根據GPL v2許可獲得許可。
- 2019年1月之後發佈的Oracle Java SE 8的公開更新將無法用於商業,但是,OpenJDK是完全開源的,可以自由使用。
- Oracle JDK的構建過程基於OpenJDK,因此OpenJDK與Oracle JDK之間沒有技術差異。
- 頂級公司正在使用Oracle JDK,Open JDK不太受歡迎。
- Oracle JDK具有良好的GC選項和更好的渲染器,而OpenJDK具有更少的GC選項
- 在響應性和JVM性能方面,Oracle JDK提供了更好的性能。
- Oracle JDK在運行JDK時不會產生任何問題,而OpenJDK有時會產生一些問題。
- Oracle JDK將從其10.0.X版本將收費,用戶必須付費或必須依賴OpenJDK才能使用其免費版本。
- Oracle JDK完全由Oracle公司開發,而Open JDK項目由IBM,Apple,SAP AG,Redhat等頂級公司加入和合作。
1.2 JVM體系
-
JDK(Java Development Kit)是 Java語言的軟體開發工具包,也是整個java開發的核心,它包含了JRE和開發工具包
-
JRE(Java Runtime Environment),Java運行環境,包含了JVM和Java的核心類庫(Java API)
-
JVM(Java Virtual Machine),Java虛擬機,它是運行在操作系統之上的,它與硬體沒有直接的交互
所謂“一次編碼,隨處運行“正是基於不同系統下的jvm幫你掩蓋了系統之間介面的差異:
總結
jdk是開發人員的工具包,它包含了java的運行環境和虛擬機,而一次編寫到處運行就是基於jvm
1.3 各種虛擬機
1.3.1 清單
1、Sun Classic VM
世界上第一款商用 Java 虛擬機。
1996年隨著Java1.0的發佈而發佈,JDK1.4時完全被淘汰
2、BEA JRockit
專註於服務端應用,號稱是世界上最快的JVM
後來被 Oracle收購;Oracle JRockit (原來的 Bea JRockit)
3、IBM公司的 J9VM
全稱:IBM Technology for Java Virtual Machine,簡稱IT4J,內部代號:J9
是 IBM 自己開發的一款 JVM
市場定位於HotSpot接近,伺服器端、桌面應用、嵌入式等多用途VM
4、HotSpot VM(現在最常用)
它是Sun JDK和OpenJDK中所帶的虛擬機,也是目前使用範圍最廣的Java虛擬機。
5、其他
(TaobaoJVM 、Graal VM、Azul VM、Liquid VM、Apache Harmony、)虛擬機
1.3.2 查看
shawn@macpro:~ > java -version
java version "1.8.0_181"
Java(TM) SE Runtime Environment (build 1.8.0_181-b13)
Java HotSpot(TM) 64-Bit Server VM (build 25.181-b13, mixed mode)
-
hotspot虛擬機
-
Client VM是專門為快速啟動和小記憶體(small footprints)而優化的,像GUI就很適合
-
Server VM是專門為高性能應用而優化的,如伺服器應用
-
版本是基於tag為1.8.0_181
1.4 jvm整體架構
1.4.1 java運行過程
1.源碼編譯:通過Java源碼編譯器將Java代碼編譯成JVM位元組碼(.class文件)
2.類載入:通過ClassLoader及其子類來完成JVM的類載入
3.類執行:位元組碼被裝入記憶體,進入JVM虛擬機,被解釋器解釋執行
1.4.2 jvm模型
由上面的圖可以看出,JVM虛擬機中主要是由三部分構成,分別是類載入子系統、運行時數據區、執行引擎。
類載入子系統
Java虛擬機把描述類的數據從Class文件載入到記憶體,並對數據進行校驗、轉換解析和初始化,最終形成可以被虛擬機直接使用的Java類型。
運行時數據區
Java虛擬機在執行Java程式的過程中會把它所管理的記憶體劃分為若幹個不同的數據區域。
這些區域有各自的用途,以及創建和銷毀的時間,有的區域隨著虛擬機進程的啟動而一直存在,有些區域則是依賴用戶線程的啟動和結束而建立和銷毀。
執行引擎
執行引擎用於執行JVM位元組碼指令,主要有兩種方式,分別是解釋執行和編譯執行,區別在於,解釋執行是在執行時翻譯成虛擬機指令執行,而編譯執行是在執行之前先進行編譯再執行。
解釋執行啟動快,執行效率低。編譯執行,啟動慢,執行效率高。
垃圾回收器就是自動管理運行數據區的記憶體,將無用的記憶體占用進行清除,釋放記憶體資源。
本地方法庫、本地庫介面
在jdk的底層中,有一些實現是需要調用本地方法完成的(使用c或c++寫的方法),就是通過本地庫介面調用完成的。比如:System.currentTimeMillis()方法。
2、類文件結構
瞭解jvm後續的一切動作,先從位元組碼開始。它是一切發生的源頭。
2.1 測試案例
2.1.1 源代碼
package com.itheima.jvm.demo;
public class ClassStruct {
private static String name = "JVM";
public static void main(String[] args) {
System.out.println("Hello " + name);
}
}
2.1.2 編譯
1)maven定義編譯的版本
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
</plugins>
</build>
2)編譯
mvn clean compile
2.2 位元組碼結構
2.2.1 二進位概覽
1)vscode打開
2)class文件是一個二進位文件,轉化後是16進位展示,實際上class文件就是一張表,它由以下數據項構成,這些數據項從頭到尾嚴格按照以下順序排列:
類型 | 名稱 | 數量 | 描述 |
---|---|---|---|
u4 | magic | 1 | 魔數 |
u2 | minor_version | 1 | 次版本號 |
u2 | major_version | 1 | 主版本號 |
u2 | constant_pool_count | 1 | 常量個數 |
cp_info | constant_pool | constant_pool_count - 1 | 具體常量 |
u2 | access_flags | 1 | 訪問標誌 |
u2 | this_class | 1 | 類索引 |
u2 | super_class | 1 | 父類索引 |
u2 | interfaces_count | 1 | 介面索引 |
u2 | interfaces | interfaces_count | 具體介面 |
u2 | fields_count | 1 | 欄位個數 |
field_info | fields | fields_count | 具體欄位 |
u2 | methods_count | 1 | 方法個數 |
method_info | methods | methods_count | 具體方法 |
u2 | attributes_count | 1 | 屬性個數 |
attribute_info | attributes | attributes_count | 具體屬性 |
3)圖示如下:
2.2.2 魔數與版本
1)魔數:
CAFEBABE,咖啡寶寶,固定的。
2)版本號:
34,換成10進位就是52
jdk的版本標記映射關係:
說明編譯用的是jdk8,我們改成1.6,重新執行 mvn clean compile ,再來查看class文件試試:
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.6</source>
<target>1.6</target>
</configuration>
</plugin>
</plugins>
</build>
擴展
在開發中,經常會遇到類似Unsupported major.minor version 51.0的錯誤,一般情況下都是JDK版本不匹配造成的。
雖然jdk代碼在執行時基本上向下相容,但是!開發環境和伺服器環境jdk最好一致,不要嘗試這個坑。
區分和理解兩個環境:編譯環境,運行環境
2.2.3 常量池
再往下遵從相同的規律: 計數器(標註後面有多少個) + 對應個數的結構體
我們以常量池為例:
1)位置
2)結構說明
常量池記錄了jvm內的一堆常量信息,這部分由 【2個位元組計數】 + 【n個cp_info結構】組成
其中cp_info有多種類型:
- 直接類型,存的就是當前值,這種像Integer,Long等長度都是確定的
- 引用類型,存的是指向其他位置的指針
附:綠色代表指針,橙色代表直接類型
3)案例
下麵以String為例,String是一種引用類,它會指向一個utf8類型來存儲真實的信息
jdk提供了一個工具,javap,可以查看常量列表的詳細內容:
javap -v ClassStruct.class
2.2.4 其他信息
1)說明
常量池之後,是緊挨的一系列信息,這些信息大同小異,無非就是值、或者引用
(參考上面2.3.3里的表格和圖例)
- 訪問標記:public abstract 等信息
- 類索引,class類型,最終指向一個utf8,標記當前類的名字
- 父類,同上
- 介面,2位元組記錄數量,後面記錄多個介面類型
- 接下來是欄位、方法、屬性,都是2位元組記錄後面多少個,後面緊跟對應的結構體類型
2)註意事項
要看懂javap後的格式,明白這些格式,可以輕鬆看懂class結構
類型 | 標識符 | 案例 | 說明 |
---|---|---|---|
數組 | [ | [Ljava.lang.String | String數組 |
對象 | L | Lcom.test.Demo | |
基本類型 | 大寫字母開頭 | B=byte,I=int…… | |
組合類型
類型 | 案例 | 說明 |
---|---|---|
類里的屬性、欄位、方法等 | com.test.Demo.name:Ljava.lang.String | 英文點號隔開 |
標識什麼類型 | com.test.Demo.getName:()Ljava.lang.String | 英文冒號隔開 |
方法 | (參數類型)返回值類型 | 英文括弧,後面是返回值類型 |
3)實例分析
本文由
傳智教育博學谷
教研團隊發佈。如果本文對您有幫助,歡迎
關註
和點贊
;如果您有任何建議也可留言評論
或私信
,您的支持是我堅持創作的動力。轉載請註明出處!