首頁>技術>

本文9321字,閱讀大約需要20~30分鐘

前言無論什麼級別的Java從業者,JVM都是進階時必須邁過的坎。不管是工作還是面試中,JVM都是必考題。如果不懂JVM的話,薪酬會非常吃虧(近70%的面試者掛在JVM上了)。

請你談談你對JVM的理解?JVM類載入器是怎麼樣的?有幾種?什麼是OOM,什麼是StackOverFlowError? 怎麼分析?JVM常用調優引數有哪些?GC有幾種演算法?分別是怎麼執行的?你知道JProfiler嗎,怎麼分析Dump檔案?

第一次看到這些真真實實的面試題的時候,我~

這都什麼玩意???????

經過一段時間的研究!!接下來,我將以大白話從頭到尾給大家講講Java虛擬機器!!

不對的地方還請大家指正~

1、什麼是JVM?在哪?

JVM是Java Virtual Machine(Java虛擬機器)的縮寫,JVM是一種用於計算裝置的規範,它是一個虛構出來的計算機,是透過在實際的計算機上模擬模擬各種計算機功能來實現的。

百度的解釋雲裡霧裡,對於我們Java程式設計師,說白了就是:

JVM本質上是一個程式,它能識別.class 位元組碼檔案(裡面存放的是我們對.java編譯後產生的二進位制程式碼),並且能夠解析它的指令,最終呼叫作業系統上的函式,完成我們想要的操作!關於Java語言的跨平臺性,就是因為JVM,我們可以將其想象為一個抽象層,只要這個抽象層JVM正確執行了.class檔案,就能執行在各種作業系統之上了!這就是一次編譯,多次執行

對於JVM的位置:

JVM是執行在作業系統之上的,它與硬體沒有直接的互動2、JVM、JRE、JDK 的關係

JDK(Java Development Kit):Java開發工具包

JRE(Java Runtime Environment):Java執行環境

JDK = JRE + javac/java/jar 等指令工具

JRE = JVM + Java基本類庫

3、JVM體系結構

Java虛擬機器主要分為五大模組:

類裝載器的系統執行時資料區執行引擎本地方法介面垃圾收集模組方法區是一種特殊的堆疊裡面不會有垃圾,用完就彈出了,否則阻塞了main方法垃圾幾乎都在堆裡,所以JVM效能調優%99都針對於堆4、三種JVM(瞭解)

Sun公司 HotSpot(我們都用的這個)

BEA公司 JRockit

IBM公司 J9 VM

5、類載入器

作用:載入.Class位元組碼檔案

1、回顧new物件的過程
public class Student {    //私有屬性    private String name;    //構造方法    public Student(String name) {        this.name = name;    }}

類似模板、模板是抽象的;物件是具體的,是對抽象的例項化

//執行時,JVM將Test的資訊放入方法區public class Test{    //main方法本身放入方法區  public static void main(String[] args){        //s1、s2、s3為不同物件        Student s1 = new Student("zsr");  //引用放在棧裡,具體的例項放在堆裡        Student s2 = new Student("gcc");        Student s3 = new Student("BareTH");        System.out.println(s1.hashCode());        System.out.println(s2.hashCode());        System.out.println(s3.hashCode());        //class1、class2、class3為同一個物件        Class<? extends Student> class1 = s1.getClass();        Class<? extends Student> class2 = s2.getClass();        Class<? extends Student> class3 = s3.getClass();        System.out.println(class1.hashCode());        System.out.println(class2.hashCode());        System.out.println(class3.hashCode());    }}

根據結果,我們發現:

s1、s2、s3的hashcode是不同的,因為是三個不同的物件,物件是具體的class1、class2、class3的hashcode是相同的,因為這是類模板,模板是抽象的

我們畫圖分析以下new一個物件的流程:

首先Class Loader讀取位元組碼.class檔案,載入初始化生成Student模板類透過Student模板類new出三個物件

那麼Class Loader具體是怎麼執行我們的.class位元組碼檔案呢,這就引出了我們類載入器~

2、類載入器的類別

我們編寫這樣一個程式

根據返回結果,我們來講解以下三種載入器:

級別從高到底

1.啟動類(根)載入器:BootstrapClassLoader

c++編寫,載入java核心庫 java.*,構造拓展類載入器和應用程式載入器。根載入器載入拓展類載入器,並且將拓展類載入器的父載入器設定為根載入器,然後再載入應用程式載入器,應將應用程式載入器的父載入器設定為拓展類載入器由於引導類載入器涉及到虛擬機器本地實現細節,我們無法直接獲取到啟動類載入器的引用;這就是上面那個程式我們第三個結果為null的原因。載入檔案存在位置

2. 拓展類載入器:PlatformClassLoader

java編寫,載入擴充套件庫,開發者可以直接使用標準擴充套件類載入器。java9之前為ExtClassloader,Java9以後改名為PlatformClassLoader載入檔案存在位置

3.應用程式載入器:AppClassLoader

1.java編寫,載入程式所在的目錄

2. 是Java預設的類載入器

4.使用者自定義類載入器:CustomClassLoader

1.java編寫,使用者自定義的類載入器,可載入指定路徑的class檔案

6、雙親委派機制1、什麼是雙親委派機制類載入器收到類載入的請求將這個請求向上委託給父類載入器去完成,一直向上委託,直到根載入器BootstrapClassLoader根載入器檢查是否能夠載入當前類,能載入就結束,使用當前的載入器;否則就丟擲異常,通知子載入器進行載入;自載入器重複該步驟。2、作用

舉個例子:我們重寫以下java.lang包下的String類

發現報錯了,這就是雙親委派機制起的作用,當類載入器委託到根載入器的時候,String類已經被根載入器載入過一遍了,所以不會再載入,從一定程度上防止了危險程式碼的植入!!

作用總結:

1. 防止重複載入同一個.class。透過不斷委託父載入器直到根載入器,如果父載入器載入過了,就不用再載入一遍。保證資料安全。2. 保證系統核心.class,如上述的String類不能被篡改。透過委託方式,不會去篡改核心.class,即使篡改也不會去載入,即使載入也不會是同一個.class物件了。不同的載入器載入同一個.class也不是同一個class物件。這樣保證了class執行安全。7、沙箱安全機制

這裡引用了這篇博文引用連結,瞭解即可

什麼是沙箱?

Java安全模型的核心就是Java沙箱(sandbox)

1. 沙箱是一個限制程式執行的環境。沙箱機制就是將 Java 程式碼限定在虛擬機器(JVM)特定的執行範圍中,並且嚴格限制程式碼對本地系統資源訪問,透過這樣的措施來保證對程式碼的有效隔離,防止對本地系統造成破壞。沙箱主要限制系統資源訪問,系統資源包括CPU、記憶體、檔案系統、網路。不同級別的沙箱對這些資源訪問的限制也可以不一樣。

所有的Java程式執行都可以指定沙箱,可以定製安全策略。

java中的安全模型演進

在Java中將執行程式分成原生代碼和遠端程式碼兩種

原生代碼可信任,可以訪問一切本地資源。遠端程式碼不可信信在早期的Java實現中,安全依賴於沙箱 (Sandbox) 機制。

如下圖所示

如此嚴格的安全機制也給程式的功能擴充套件帶來障礙,比如當用戶希望遠端程式碼訪問本地系統的檔案時候,就無法實現。

因此在後續的 Java1.1 版本中,針對安全機制做了改進,增加了安全策略,允許使用者指定程式碼對本地資源的訪問許可權。

如下圖所示

在Java1.2版本中,再次改進了安全機制,增加了程式碼簽名。

不論原生代碼或是遠端程式碼,都會按照使用者的安全策略設定,由類載入器載入到虛擬機器中許可權不同的執行空間,來實現差異化的程式碼執行許可權控制。

如下圖所示

當前最新的安全機制實現,則引入了域 (Domain) 的概念。

虛擬機器會把所有程式碼載入到不同的系統域和應用域系統域部分專門負責與關鍵資源進行互動應用域部分則透過系統域的部分代理來對各種需要的資源進行訪問。虛擬機器中不同的受保護域 (Protected Domain),對應不一樣的許可權 (Permission)。存在於不同域中的類檔案就具有了當前域的全部許可權,如下圖所示組成沙箱的基本元件1. 位元組碼校驗器(bytecode verifier)

確保Java類檔案遵循Java語言規範。這樣可以幫助Java程式實現記憶體保護。但並不是所有的類檔案都會經過位元組碼校驗,比如核心類(如上述java.lang.String)。

2. 類裝載器(class loader)

其中類裝載器在3個方面對Java沙箱起作用

它防止惡意程式碼去幹涉善意的程式碼;它守護了被信任的類庫邊界;它將程式碼歸入保護域,確定了程式碼可以進行哪些操作。

虛擬機器為不同的類載入器載入的類提供不同的名稱空間,名稱空間由一系列唯一的名稱組成,每一個被裝載的類將有一個名字,這個名稱空間是由Java虛擬機器為每一個類裝載器維護的,它們互相之間甚至不可見。

類裝載器採用的機制是雙親委派模式。

從最內層JVM自帶類載入器開始載入,外層惡意同名類得不到載入從而無法使用;由於嚴格透過包來區分了訪問域,外層惡意的類透過內建程式碼也無法獲得許可權訪問到內層類,破壞程式碼就自然無法生效。存取控制器(access controller):存取控制器可以控制核心API對作業系統的存取許可權,而這個控制的策略設定,可以由使用者指定。安全管理器(security manager):是核心API和作業系統之間的主要介面。實現許可權控制,比存取控制器優先順序高。安全軟體包(security package):java.security下的類和擴充套件包下的類,允許使用者為自己的應用增加新的安全特性,包括:安全提供者訊息摘要數字簽名加密鑑別8、Native本地方法介面

JNI:Java Native Interface

本地介面的作用是融合不同的程式語言為Java所用,它的初衷是融合C/C++程式

native:凡是帶native關鍵字的,說明java的作用範圍達不到了,會去呼叫底層c語言的庫!進入本地方法棧,呼叫本地方法介面JNI,拓展Java的使用,融合不同的語言為Java所用

Java誕生的時候C、C++橫行,為了立足,必須要能呼叫C、C++的程式於是在記憶體區域中專門開闢了一塊標記區域:Native Method Stack,登記Native方法最終在執行引擎執行的的時候透過JNI(本地方法介面)載入本地方法庫的方法

目前該方法使用的越來越少了,除非是與硬體有關的應用,比如透過Java程式驅動印表機或者Java系統管理生產裝置,在企業級應用中已經比較少見。因為現在的異構領域間通訊很發達,比如可以使用 Socket通訊,也可以使用 Web service等等,瞭解即可!

9、PC暫存器

程式計數器:Program Counter Register

每個執行緒都有一個程式計數器,是執行緒私有的,就是一個指標,指向方法區中的方法位元組碼(用來儲存指向像一條指令的地址,也即將要執行的指令程式碼),在執行引擎讀取下一條指令,是一個非常小的記憶體空間,幾乎可以忽略不計10、方法區

方法區:Method Area

方法區是被所有執行緒共享,所有欄位和方法位元組碼,以及一些特殊方法,如建構函式,介面程式碼也在此定義,簡單說,所有定義的方法的資訊都儲存在該區域,此區域屬於共享區間;方法區與Java堆一樣,是各個執行緒共享的記憶體區域,用於儲存已被虛擬機器載入的類資訊、常量、靜態變數、即時編譯器編譯後的程式碼等資料。雖然Java 虛擬機器規範把方法區描述為堆的一個邏輯部分,但是它卻有一個別名叫做Non-Heap(非堆),目的應該是與Java 堆區分開來。1. 方法區中有啥?靜態變數(static)常量(final)類資訊(構造方法、介面定義)執行時的常量池2. 建立物件記憶體分析建立一個物件時,方法區中會生成對應類的抽象模板;還有對應的常量池、靜態變數、類資訊、常量我們透過類模板去new物件的時候堆中存放例項物件棧中存放物件的引用,每個物件對應一個地址指向堆中相同地址的例項物件

例如這個例子中,生成了對應的Person模板類,name常量“zsr”放在常量池中,三個物件的引用放在棧中,該引用指向放在堆中的三個例項物件。

這就是堆、棧、方法區的互動關係

下一篇介紹剩下的部分

13
最新評論
  • BSA-TRITC(10mg/ml) TRITC-BSA 牛血清白蛋白改性標記羅丹明
  • 談談kafka訊息消費中的零複製技術