為啥JAVA虛擬機不開發系統_理解Java虛擬機體系結構

1 概述

眾所周知,Java支持平臺無關性、安全性和網絡移動性。而Java平臺由Java虛擬機和Java核心類所構成,它為純Java程序提供了統一的編程接口,而不管下層操作系統是什么。正是得益于Java虛擬機,它號稱的“一次編譯,到處運行”才能有所保障。

1.1 Java程序執行流程

Java程序的執行依賴于編譯環境和運行環境。源碼代碼轉變成可執行的機器代碼,由下面的流程完成:

588c69bec7ca6b41d0d68124afeaaa4a.png

Java技術的核心就是Java虛擬機,因為所有的Java程序都在虛擬機上運行。Java程序的運行需要Java虛擬機、Java API和Java Class文件的配合。Java虛擬機實例負責運行一個Java程序。當啟動一個Java程序時,一個虛擬機實例就誕生了。當程序結束,這個虛擬機實例也就消亡。

8e22783540e5b2c013f841fdb3036bc8.png

Java的跨平臺特性,因為它有針對不同平臺的虛擬機。

1.2 Java虛擬機

Java虛擬機的主要任務是裝載class文件并且執行其中的字節碼。由下圖可以看出,Java虛擬機包含一個類裝載器(class loader),它可以從程序和API中裝載class文件,Java API中只有程序執行時需要的類才會被裝載,字節碼由執行引擎來執行。

146bbf2f4630ab6e2e51deffff86cf82.png

當Java虛擬機由主機操作系統上的軟件實現時,Java程序通過調用本地方法和主機進行交互。Java方法由Java語言編寫,編譯成字節碼,存儲在class文件中。本地方法由C/C++/匯編語言編寫,編譯成和處理器相關的機器代碼,存儲在動態鏈接庫中,格式是各個平臺專有。所以本地方法是聯系Java程序和底層主機操作系統的連接方式。

由于Java虛擬機并不知道某個class文件是如何被創建的,是否被篡改一無所知,所以它實現了一個class文件檢測器,確保class文件中定義的類型可以安全地使用。class文件檢驗器通過四趟獨立的掃描來保證程序的健壯性:

class文件的結構檢查

類型數據的語義檢查

字節碼驗證

符號引用驗證

Java虛擬機在執行字節碼時還進行其它的一些內置的安全機制的操作,他們作為Java編程語言保證Java程序健壯性的特性,同時也是Java虛擬機的特性:

類型安全的引用轉換

結構化的內存訪問

自動垃圾收集

數組邊界檢查

空引用檢查

1.3 Java虛擬機數據類型

Java虛擬機通過某些數據類型來執行計算。數據類型可以分為兩種:基本類型和引用類型,如下圖:

1dc8d6e80af8afff931cb17a7d79e436.png

但boolean有點特別,當編譯器把Java源碼編譯為字節碼時,它會用int或byte表示boolean。在Java虛擬機中,false是由0表示,而true則由所有非零整數表示。和Java語言一樣,Java虛擬機的基本類型的值域在任何地方都是一致的,不管主機平臺是什么,一個long在任何虛擬機中總是一個64位二進制補碼的有符號整數。

對于returnAddress,這個基本類型被用來實現Java程序中的finally子句,Java程序員不能使用這個類型,它的值指向一條虛擬機指令的操作碼。

2 體系結構

在 Java虛擬機規范中,一個虛擬機實例的行為是分別按照子系統、內存區、數據類型和指令來描述的,這些組成部分一起展示了抽象的虛擬機的內部體系結構。

b39c93ec7d4f25e5de19f4f022e00636.png

2.1 class文件

Java class文件包含了關于類或接口的所有信息。class文件的“基本類型”如下:

u1

1個字節,無符號類型

u2

2個字節,無符號類型

u4

4個字節,無符號類型

u8

8個字節,無符號類型

class文件包含的內容:

48304ba5e6f9fe08f3fa1abda7d326ab.png

ClassFile {

u4 magic; //魔數:0xCAFEBABE,用來判斷是否是Java class文件

u2 minor_version; //次版本號

u2 major_version; //主版本號

u2 constant_pool_count; //常量池大小

cp_info constant_pool[constant_pool_count-1]; //常量池

u2 access_flags; //類和接口層次的訪問標志(通過|運算得到)

u2 this_class; //類索引(指向常量池中的類常量)

u2 super_class; //父類索引(指向常量池中的類常量)

u2 interfaces_count; //接口索引計數器

u2 interfaces[interfaces_count]; //接口索引集合

u2 fields_count; //字段數量計數器

field_info fields[fields_count]; //字段表集合

u2 methods_count; //方法數量計數器

method_info methods[methods_count]; //方法表集合

u2 attributes_count; //屬性個數

attribute_info attributes[attributes_count]; //屬性表

}

48304ba5e6f9fe08f3fa1abda7d326ab.png

2.2?類裝載器子系統

類裝載器子系統負責查找并裝載類型信息。其實Java虛擬機有兩種類裝載器:系統裝載器和用戶自定義裝載器。前者是Java虛擬機實現的一部分,后者則是Java程序的一部分。

fb32614565ca268c2f11d88129dc3d83.png

啟動類裝載器(bootstrap class loader):它用來加載 Java 的核心庫,是用原生代碼來實現的,并不繼承自java.lang.ClassLoader。

擴展類裝載器(extensions class loader):它用來加載 Java 的擴展庫。Java 虛擬機的實現會提供一個擴展庫目錄。該類加載器在此目錄里面查找并加載 Java 類。

應用程序類裝載器(application class loader):它根據 Java 應用的類路徑(CLASSPATH)來加載 Java 類。一般來說,Java 應用的類都是由它來完成加載的。可以通過 ClassLoader.getSystemClassLoader()來獲取它。

除了系統提供的類裝載器以外,開發人員可以通過繼承 java.lang.ClassLoader類的方式實現自己的類裝載器,以滿足一些特殊的需求。

類裝載器子系統涉及Java虛擬機的其它幾個組成部分以及來自java.lang庫的類。ClassLoader定義的方法為程序提供了訪問類裝載器機制的接口。此外,對于每一個被裝載的類型,Java虛擬機都會為它創建一個java.lang.Class類的實例來代表該類型。和其它對象一樣,用戶自定義的類裝載器以及Class類的實例放在內存中的堆區,而裝載的類型信息則位于方法區。

類裝載器子系統除了要定位和導入二進制class文件外,還必須負責驗證被導入類的正確性,為類變量分配并初始化內存,以及解析符號引用。這些動作還需要按照以下順序進行:

裝載(查找并裝載類型的二進制數據)

連接(執行驗證:確保被導入類型的正確性;準備:為類變量分配內存,并將其初始化為默認值;解析:把類型中的符號引用轉換為直接引用)

初始化(類變量初始化為正確初始值)

2.3 方法區

在Java虛擬機中,關于被裝載的類型信息存儲在一個方法區的內存中。當虛擬機裝載某個類型時,它使用類裝載器定位相應的class文件,然后讀入這個class文件并將它傳輸到虛擬機中,接著虛擬機提取其中的類型信息,并將這些信息存儲到方法區。方法區也可以被垃圾回收器收集,因為虛擬機允許通過用戶定義的類裝載器來動態擴展Java程序。

方法區中存放了以下信息:

這個類型的全限定名(如全限定名java.lang.Object)

這個類型的直接超類的全限定名

這個類型是類類型還是接口類型

這個類型的訪問修飾符(public, abstract, final的某個子集)

任何直接超接口的全限定名的有序列表

該類型的常量池(一個有序集合,包括直接常量[string, integer和floating point常量]和對其它類型、字段和方法的符號引用)

字段信息(字段名、類型、修飾符)

方法信息(方法名、返回類型、參數數量和類型、修飾符)

除了常量以外的所有類(靜態)變量

指向ClassLoader類的引用(每個類型被裝載時,虛擬機必須跟蹤它是由啟動類裝載器還是由用戶自定義類裝載器裝載的)

指向Class類的引用(對于每一個被裝載的類型,虛擬機相應地為它創建一個java.lang.Class類的實例。比如你有一個到java.lang.Integer類的對象的引用,那么只需要調用Integer對象引用的getClass()方法,就可以得到表示java.lang.Integer類的Class對象)

2.4 堆

Java程序在運行時創建的所有類實例或數組(數組在Java虛擬機中是一個真正的對象)都放在同一個堆中。由于Java虛擬機實例只有一個堆空間,所以所有線程都將共享這個堆。需要注意的是,Java虛擬機有一條在堆中分配對象的指令,卻沒有釋放內存的指令,因為虛擬機把這個任務交給垃圾收集器處理。Java虛擬機規范并沒有強制規定垃圾收集器,它只要求虛擬機實現必須“以某種方式”管理自己的堆空間。比如某個實現可能只有固定大小的堆空間,當空間填滿,它就簡單拋出OutOfMemory異常,根本不考慮回收垃圾對象的問題,但卻是符合規范的。

Java虛擬機規范并沒有規定Java對象在堆中如何表示,這給虛擬機的實現者決定怎么設計。一個可能的堆設計如下:

63d26797b7e0d666d192f4ad3217c8b2.png

一個句柄池,一個對象池。一個對象的引用就是一個指向句柄池的本地指針。這種設計的好處有利于堆碎片的整理,當移動對象池中的對象時,句柄部分只需更改一下指針指向對象的新地址即可。缺點是每次訪問對象的實例變量都要經過兩次指針傳遞。

2.5 Java棧

每當啟動給一個線程時,Java虛擬機會為它分配一個Java棧。Java棧由許多棧幀組成,一個棧幀包含一個Java方法調用的狀態。當線程調用一個Java方法時,虛擬機壓入一個新的棧幀到該線程的Java棧中,當該方法返回時,這個棧幀就從Java棧中彈出。Java棧存儲線程中Java方法調用的狀態--包括局部變量、參數、返回值以及運算的中間結果等。Java虛擬機沒有寄存器,其指令集使用Java棧來存儲中間數據。這樣設計的原因是為了保持Java虛擬機的指令集盡量緊湊,同時也便于Java虛擬機在只有很少通用寄存器的平臺上實現。另外,基于棧的體系結構,也有助于運行時某些虛擬機實現的動態編譯器和即時編譯器的代碼優化。

2.5.1 棧幀

棧幀由局部變量區、操作數棧和幀數據區組成。當虛擬機調用一個Java方法時,它從對應類的類型信息中得到此方法的局部變量區和操作數棧的大小,并根據此分配棧幀內存,然后壓入Java棧中。

2.5.1.1 局部變量區

局部變量區被組織為以字長為單位、從0開始計數的數組。字節碼指令通過從0開始的索引使用其中的數據。類型為int, float, reference和returnAddress的值在數組中占據一項,而類型為byte, short和char的值在存入數組前都被轉換為int值,也占據一項。但類型為long和double的值在數組中卻占據連續的兩項。

deffaa192aacf3fbb1f6d7627b31b523.png

2.5.1.2 操作數棧

和局部變量區一樣,操作數棧也是被組織成一個以字長為單位的數組。它通過標準的棧操作訪問--壓棧和出棧。由于程序計數器無法被程序指令直接訪問,Java虛擬機的指令是從操作數棧中取得操作數,所以它的運行方式是基于棧而不是基于寄存器。虛擬機把操作數棧作為它的工作區,因為大多數指令都要從這里彈出數據,執行運算,然后把結果壓回操作數棧。

2.5.1.3 幀數據區

除了局部變量區和操作數棧,Java棧幀還需要幀數據區來支持常量池解析、正常方法返回以及異常派發機制。每當虛擬機要執行某個需要用到常量池數據的指令時,它會通過幀數據區中指向常量池的指針來訪問它。除了常量池的解析外,幀數據區還要幫助虛擬機處理Java方法的正常結束或異常中止。如果通過return正常結束,虛擬機必須恢復發起調用的方法的棧幀,包括設置程序計數器指向發起調用方法的下一個指令;如果方法有返回值,虛擬機需要將它壓入到發起調用的方法的操作數棧。為了處理Java方法執行期間的異常退出情況,幀數據區還保存一個對此方法異常表的引用。

2.6 程序計數器

對于一個運行中的Java程序而言,每一個線程都有它的程序計數器。程序計數器也叫PC寄存器。程序計數器既能持有一個本地指針,也能持有一個returnAddress。當線程執行某個Java方法時,程序計數器的值總是下一條被執行指令的地址。這里的地址可以是一個本地指針,也可以是方法字節碼中相對該方法起始指令的偏移量。如果該線程正在執行一個本地方法,那么此時程序計數器的值是“undefined”。

2.7 本地方法棧

任何本地方法接口都會使用某種本地方法棧。當線程調用Java方法時,虛擬機會創建一個新的棧幀并壓入Java棧。當它調用的是本地方法時,虛擬機會保持Java棧不變,不再在線程的Java棧中壓入新的棧,虛擬機只是簡單地動態連接并直接調用指定的本地方法。

其中方法區和堆由該虛擬機實例中所有線程共享。當虛擬機裝載一個class文件時,它會從這個class文件包含的二進制數據中解析類型信息,然后把這些類型信息放到方法區。當程序運行時,虛擬機會把所有該程序在運行時創建的對象放到堆中。

像其它運行時內存區一樣,本地方法棧占用的內存區可以根據需要動態擴展或收縮。

3 執行引擎

在Java虛擬機規范中,執行引擎的行為使用指令集定義。實現執行引擎的設計者將決定如何執行字節碼,實現可以采取解釋、即時編譯或直接使用芯片上的指令執行,還可以是它們的混合。

執行引擎可以理解成一個抽象的規范、一個具體的實現或一個正在運行的實例。抽象規范使用指令集規定了執行引擎的行為。具體實現可能使用多種不同的技術--包括軟件方面、硬件方面或樹種技術的結合。作為運行時實例的執行引擎就是一個線程。

運行中Java程序的每一個線程都是一個獨立的虛擬機執行引擎的實例。從線程生命周期的開始到結束,它要么在執行字節碼,要么執行本地方法。

3.1 指令集

方法的字節碼流由Java虛擬機的指令序列構成。每一條指令包含一個單字節的操作碼,后面跟隨0個或多個操作數。操作碼表示需要執行的操作;操作數向Java虛擬機提供執行操作碼需要的額外信息。當虛擬機執行一條指令時,可能使用當前常量池中的項、當前幀的局部變量中的值或者位于當前幀操作數棧頂端的值。

抽象的執行引擎每次執行一條字節碼指令。Java虛擬機中運行的程序的每個線程(執行引擎實例)都執行這個操作。執行引擎取得操作碼,如果操作碼有操作數,就取得它的操作數。它執行操作碼和跟隨的操作數規定的動作,然后再取得下一個操作碼。這個執行字節碼的過程在線程完成前將一直持續,通過從它的初始方法返回,或者沒有捕獲拋出的異常都可以標志著線程的完成。

4 本地方法接口

Java本地接口,也叫JNI(Java Native Interface),是為可移植性準備的。本地方法接口允許本地方法完成以下工作:

傳遞或返回數據

操作實例變量

操作類變量或調用類方法

操作數組

對堆的對象加鎖

裝載新的類

拋出異常

捕獲本地方法調用Java方法拋出的異常

捕獲虛擬機拋出的異步異常

指示垃圾收集器某個對象不再需要

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/394079.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/394079.shtml
英文地址,請注明出處:http://en.pswp.cn/news/394079.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Android WindowManager和WindowManager.LayoutParams的使用以及實現懸浮窗口的方法

1.理清概念 我們使用過Dialog和PopupWindow,還有Toast,它們都顯示在Activity之上。那么我們首先需要理解的是android中是如何去繪制這些UI的呢?這里我只講我所理解的,首先看一層次圖(盜用網絡)首先我們看到左邊的Activity層&#…

leetcode面試題 04.03. 特定深度節點鏈表(bfs)

給定一棵二叉樹,設計一個算法,創建含有某一深度上所有節點的鏈表(比如,若一棵樹的深度為 D,則會創建出 D 個鏈表)。返回一個包含所有深度的鏈表的數組。示例:輸入:[1,2,3,4,5,null,7…

Java集合中的細節

integer數據對比 對于Integer var ? 在-128至127范圍內的賦值,Integer對象是在IntegerCache.cache產生,會復用已有對象,這個區間內的Integer值可以直接使用進行判斷,但是這個區間之外的所有數據,都會在堆上產生&…

css擴展語言_如何決定是否應該鏈接或擴展CSS類

css擴展語言by Sarah Dayan通過莎拉達揚 如何決定是否應該鏈接或擴展CSS類 (How to decide whether you should chain or extend CSS classes) If you’re building an app or a website that changes often, modular CSS methods solve many issues. Instead of copying your…

vue 是否有word編輯控件_GitHub - C84882428/editor-ui: vue集成 tinymce 富文本編輯器,增加導入 word 模板...

editor-uivue 集成 tinymce 富文本編輯器自定義 tinymce 富文本編輯器,在原來的編輯器中增加上傳 word 模板最終展示效果:主要代碼:整體思路:1,在編輯器原來的基礎上增加上傳模板按鈕2, 前端上傳 word 模板3, 服務端接收將 word 轉換為html 返回前端4, 前端拿到服務端返回的值,…

Android開發系列之屏幕密度和單位轉換

由于Android的開源性,所以目前市面上面Android手機的分辨率特別多,這樣的話就給我適配帶來了一定的難度。要想做好適配,我們首先應該明白什么是分辨率、PPI、屏幕大小等概念,還有在不同的屏幕密度下,各個單位之間的轉換…

java集合AbstractMap_Java 集合中的 AbstractMap 抽象類

Java 集合中的 AbstractMap 抽象類jdk1.8.0_144AbstractMap 抽象類實現了一些簡單且通用的方法, 本身并不難但在這個抽象類中有兩個方法非常值得關注, keySet 和 values 方法源碼的實現可以說是教科書式的典范抽象類通常作為一種骨架實現, 為各自子類實現公共的方法上一篇我們講…

leetcode392. 判斷子序列(動態規劃)

給定字符串 s 和 t &#xff0c;判斷 s 是否為 t 的子序列。 你可以認為 s 和 t 中僅包含英文小寫字母。字符串 t 可能會很長&#xff08;長度 ~ 500,000&#xff09;&#xff0c;而 s 是個短字符串&#xff08;長度 <100&#xff09;。 字符串的一個子序列是原始字符串刪…

讓機器讀懂用戶——大數據中的用戶畫像

讓機器讀懂用戶——大數據中的用戶畫像 摘要&#xff1a; 用戶畫像(persona)的概念最早由交互設計之父Alan Cooper提出:“Personas are a concrete representation of target users.” 是指真實用戶的虛擬代表&#xff0c;是建立在一系列屬性數據之上的目標用戶模型。隨著互聯…

asp.net應用程序_如何在ASP.NET中為聊天應用程序構建鍵入指示器

asp.net應用程序by Neo Ighodaro由新Ighodaro 如何在ASP.NET中為聊天應用程序構建鍵入指示器 (How to build a typing indicator for your chat app in ASP.NET) A basic understanding of ASP.NET and jQuery is needed to follow this tutorial.要學習本教程&#xff0c;需要…

activeMQ在文件上傳的應用

本次試驗主要用到了activeMq和上傳插件uploadify的知識&#xff0c;感謝以下兩篇文章的作者。 1.http://itindex.net/detail/47160-java-jquery-%E4%B8%8A%E4%BC%A0 2.http://blog.csdn.net/jiuqiyuliang/article/details/47160259 本文中不再提供activeMq和uploadify的介紹。 …

java nginx 例子_Java及nginx實現文件權限控制代碼實例

我們知道&#xff0c;使用nginx作為文件下載服務器&#xff0c;可以極大地降低對后端Java服務器的負載沖擊&#xff0c;但是nginx本身并不提供授權控制&#xff0c;因此好的方案是由后端服務器實現權限控制&#xff0c;最好的方式是直接復用應用的認證體系&#xff0c;最大化的…

leetcode934. 最短的橋(dfs+bfs)

在給定的二維二進制數組 A 中&#xff0c;存在兩座島。&#xff08;島是由四面相連的 1 形成的一個最大組。&#xff09; 現在&#xff0c;我們可以將 0 變為 1&#xff0c;以使兩座島連接起來&#xff0c;變成一座島。 返回必須翻轉的 0 的最小數目。&#xff08;可以保證答…

謝煙客---------Linux之DNS服務系統的基礎知識

DNS Domain Name Server1)C/S架構&#xff1a;SOCKET通信IP PORT2&#xff09;應用層協議&#xff1a;資源子網BIND Berkerley Information Name DomainDNS由來1&#xff09;統一名字&#xff0c;自己維護 <自己查詢>解析: 基于key查找value: 查詢數據庫(二維關系的表: …

Java實現點擊導出excel頁面遮罩屏蔽,下載完成后解除遮罩

一、問題場景 最近在做數據統計功能&#xff0c;需求是導出大數據量的excel&#xff0c;時間間隔較長&#xff0c;大概需要十秒左右&#xff0c;點擊導出后&#xff0c;頁面沒有做任何處理&#xff0c;用戶也不知道是否正在導出&#xff1b;如果沒有做交互上的限制&#xff0c;…

pbs 支持 java_Linux下Java安裝與配置

安裝以JDK1.6.0_43為例下載jdk-6u43-linux-x64.bin&#xff0c;http://www.oracle.com/technetwork/java/javase/downloads/index.html增加可執行權限 chmod x jdk-6u43-linux-x64.bin&#xff0c;執行 ./jdk-6u43-linux-x64.bin 生成目錄jdk1.6.0_43拷貝到/usr/share下&#x…

使用Chatkit構建Node.js命令行聊天應用程序

by Hugo雨果 使用Chatkit構建Node.js命令行聊天應用程序 (Build a Node.js command-line chat application with Chatkit) Building chat in your app can be pretty complex. Yet, with Chatkit, implementing fully-featured chat is nothing but a few lines of code.在您的…

java 毫秒轉分鐘和秒_Java程序將毫秒轉換為分鐘和秒

Java程序將毫秒轉換為分鐘和秒在上面的程序中&#xff0c;您將學習如何在Java中將毫秒分別轉換為分鐘和秒。示例1&#xff1a;將毫秒分別轉換為分鐘和秒import java.util.concurrent.TimeUnit;public class Milliseconds {public static void main(String[] args) {long millis…

Andrew Ng機器學習之一 導論

監督學習與無監督學習 監督學習&#xff08;Supervised Learning) Ng的原文是&#xff1a; We gave the algorithm a data set that the "right answers" were given. 即給定了一個正確結果的集合供算法學習&#xff0c;強調了需要實現準備好正負樣本喂給機器。 無監…

leetcode994. 腐爛的橘子(bfs)

在給定的網格中&#xff0c;每個單元格可以有以下三個值之一&#xff1a; 值 0 代表空單元格&#xff1b; 值 1 代表新鮮橘子&#xff1b; 值 2 代表腐爛的橘子。 每分鐘&#xff0c;任何與腐爛的橘子&#xff08;在 4 個正方向上&#xff09;相鄰的新鮮橘子都會腐爛。 返回直…