欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Java虛擬機工作原理

 更新時間:2017年03月23日 10:12:20   作者:best.lei  
本文主要介紹了Java虛擬機的工作原理。具有很好的參考價值。下面跟著小編一起來看下吧

首先我想從宏觀上介紹一下Java虛擬機的工作原理。從最初的我們編寫的Java源文件(.java文件)是如何一步步執(zhí)行的,如下圖所示,首先Java源文件經(jīng)過前端編譯器(javac或ECJ)將.java文件編譯為Java字節(jié)碼文件,然后JRE加載Java字節(jié)碼文件,載入系統(tǒng)分配給JVM的內(nèi)存區(qū),然后執(zhí)行引擎解釋或編譯類文件,再由即時編譯器將字節(jié)碼轉(zhuǎn)化為機器碼。主要介紹下圖中的類加載器和運行時數(shù)據(jù)區(qū)兩個部分。

類加載

類加載指將類的字節(jié)碼文件(.class)中的二進制數(shù)據(jù)讀入內(nèi)存,將其放在運行時數(shù)據(jù)區(qū)的方法區(qū)內(nèi),然后在堆上創(chuàng)建java.lang.Class對象,封裝類在方法區(qū)內(nèi)的數(shù)據(jù)結構。類加載的最終產(chǎn)品是位于堆中的類對象,類對象封裝了類在方法區(qū)內(nèi)的數(shù)據(jù)結構,并且向JAVA程序提供了訪問方法區(qū)內(nèi)數(shù)據(jù)結構的接口。如下是類加載器的層次關系圖。

啟動類加載器(BootstrapClassLoader):在JVM運行時被創(chuàng)建,負責加載存放在JDK安裝目錄下的jre\lib的類文件,或者被-Xbootclasspath參數(shù)指定的路徑中,并且能被虛擬機識別的類庫(如rt.jar,所有的java.*開頭的類均被Bootstrap ClassLoader加載)。啟動類無法被JAVA程序直接引用。

擴展類加載器(Extension ClassLoader):該類加載器負責加載JDK安裝目錄下的\jre\lib\ext的類,或者由java.ext.dirs系統(tǒng)變量指定路徑中的所有類庫,開發(fā)者也可以直接使用擴展類加載器。

應用程序類加載器(AppClassLoader):負責加載用戶類路徑(Classpath)所指定的類,開發(fā)者可以直接使用該類加載器,如果應用程序中沒有定義過自己的類加載器,該類加載器為默認的類加載器。

用戶自定義類加載器(User ClassLoader):JVM自帶的類加載器是從本地文件系統(tǒng)加載標準的java class文件,而自定義的類加載器可以做到在執(zhí)行非置信代碼之前,自動驗證數(shù)字簽名,動態(tài)地創(chuàng)建符合用戶特定需要的定制化構建類,從特定的場所(數(shù)據(jù)庫、網(wǎng)絡中)取得java class。

注意如上的類加載器并不是通過繼承的方式實現(xiàn)的,而是通過組合的方式實現(xiàn)的。而JAVA虛擬機的加載模式是一種委派模式,如上圖中的1-7步所示。下層的加載器能夠看到上層加載器中的類,反之則不行。類加載器可以加載類但是不能卸載類。說了一大堆,還是感覺需要拿點代碼說事。

首先我們先定義自己的類加載器MyClassLoader,繼承自ClassLoader,并覆蓋了父類的findClass(String name)方法,如下:

public class MyClassLoader extends ClassLoader{
 private String loaderName; //類加載器名稱
 private String path = ""; //加載類的路徑
 private final String fileType = ".class";
 public MyClassLoader(String name){
  super(); //應用類加載器為該類的父類
  this.loaderName = name;
 }
 public MyClassLoader(ClassLoader parent,String name){
  super(parent);
  this.loaderName = name;
 }
 public String getPath(){return this.path;}
 public void setPath(String path){this.path = path;}
 @Override
 public String toString(){return this.loaderName;}
 @Override
 public Class<?> findClass(String name) throws ClassNotFoundException{
  byte[] data = loaderClassData(name);
  return this.defineClass(name, data, 0, data.length);
 }
 //讀取.class文件
 private byte[] loaderClassData(String name){
  InputStream is = null;
  byte[] data = null;
  ByteArrayOutputStream baos = new ByteArrayOutputStream();
  try {
   is = new FileInputStream(new File(path + name + fileType));
   int c = 0;
   while(-1 != (c = is.read())){
    baos.write(c);
   }
   data = baos.toByteArray();
  } catch (Exception e) {
   e.printStackTrace();
  } finally{
   try {
    if(is != null)
     is.close();
    if(baos != null)
     baos.close();
   } catch (IOException e) {
    e.printStackTrace();
   }
  }
  return data;
 }
}

我們?nèi)绾卫梦覀兌x的類加載器加載指定的字節(jié)碼文件(.class)呢?如通過MyClassLoader加載C:\\Users\\Administrator\\下的Test.class字節(jié)碼文件,代碼如下所示:

public class Client {
 public static void main(String[] args) {
  // TODO Auto-generated method stub  
  //MyClassLoader的父類加載器為系統(tǒng)默認的加載器AppClassLoader
  MyClassLoader myCLoader = new MyClassLoader("MyClassLoader");
  //指定MyClassLoader的父類加載器為ExtClassLoader
  //MyClassLoader myCLoader = new MyClassLoader(ClassLoader.getSystemClassLoader().getParent(),"MyClassLoader");
  myCLoader.setPath("C:\\Users\\Administrator\\");
  Class<?> clazz;
  try {
   clazz = myCLoader.loadClass("Test");
   Field[] filed = clazz.getFields(); //獲取加載類的屬性字段
   Method[] methods = clazz.getMethods(); //獲取加載類的方法字段
   System.out.println("該類的類加載器為:" + clazz.getClassLoader());
   System.out.println("該類的類加載器的父類為:" + clazz.getClassLoader().getParent());
   System.out.println("該類的名稱為:" + clazz.getName());
  } catch (ClassNotFoundException e) {
   // TODO Auto-generated catch block
   e.printStackTrace();
  }
 }
}

運行時數(shù)據(jù)區(qū)

字節(jié)碼的加載第一步,其后分別是認證、準備、解析、初始化,那么這些步驟又具體做了哪些工作,以及他們會對運行時數(shù)據(jù)區(qū)纏身什么影響呢?如下圖所示:

如下我們將介紹運行時數(shù)據(jù)區(qū),主要分為方法區(qū)、Java堆、虛擬機棧、本地方法棧、程序計數(shù)器。其中方法區(qū)和Java堆一樣,是各個線程共享的內(nèi)存區(qū)域,而虛擬機棧、本地方法棧、程序計數(shù)器是線程私有的內(nèi)存區(qū)。

Java堆:Java堆是Java虛擬機所管理的內(nèi)存中最大的一塊,被進程的所有線程共享,在虛擬機啟動時被創(chuàng)建。該區(qū)域的唯一目的就是存放對象實例,幾乎所有的對象實例都在這里分配內(nèi)存,隨著JIT編譯器的發(fā)展與逃逸分支技術逐漸成熟,棧上分配、標量替換等優(yōu)化技術使得對象在堆上的分配內(nèi)存變得不是那么“絕對”。Java堆是垃圾收集器管理的主要區(qū)域。由于現(xiàn)在的收集器基本都采用分代收集算法,所以Java堆中還可以分為老年代和新生代(Eden、From Survivor、To Survivor)。根據(jù)Java虛擬機規(guī)范,Java堆可以處于物理上不連續(xù)的內(nèi)存空間,只要邏輯上連續(xù)即可。該區(qū)域的大小可以通過-Xmx和-Xms參數(shù)來擴展,如果堆中沒有內(nèi)存完成實例分配,并且堆也無法擴展,將會拋出OutOfMemoryError異常。

方法區(qū):用于存儲被Java虛擬機加載的類信息、常量、靜態(tài)變量、即時編譯器編譯后的代碼等數(shù)據(jù)。不同于Java堆的是,Java虛擬機規(guī)范對方法區(qū)的限制非常寬松,可以選擇不實現(xiàn)垃圾收集。但并非數(shù)據(jù)進入了方法區(qū)就“永久”存在了,這區(qū)域內(nèi)存回收目標主要是針對常量池的回收和對類型的卸載。如果該區(qū)域內(nèi)存不足也會拋出OutOfMemoryError異常。

常量池:這個名詞可能大家也經(jīng)常見,它是方法區(qū)的一部分。Class文件除了有類的版本、字段、方法、接口等描述信息外,還有一項信息就是常量池,用于存放編譯期生成的各種字面量和符號引用。Java虛擬機運行期間,也可能將新的常量放入常量池(如String類的intern()方法)。

虛擬機棧:線程私有,生命周期與線程相同。虛擬機棧描述的是Java方法執(zhí)行的內(nèi)存模型:每個方法在執(zhí)行時都會創(chuàng)建一個棧幀用于存儲局部變量表、操作數(shù)棧、動態(tài)鏈接、方法出口等信息。每個方法從調(diào)用直至執(zhí)行完成的過程,就對應著一個棧幀在虛擬機棧中入棧到出棧的過程。如果請求的站深度大于虛擬機所允許的深度,將拋出StackOverflowError異常,虛擬機棧在動態(tài)擴展時如果無法申請到足夠的內(nèi)存,就會拋出OutOfMemoryError異常。

本地方法棧:與虛擬機棧類似,不過虛擬機棧是為虛擬機執(zhí)行Java方法(字節(jié)碼)服務,而本地方法棧則是為虛擬機使用到的Native方法服務。該區(qū)域同樣會報StackOverflowError和OutOfMemoryError異常。

程序計數(shù)器:一塊較小的內(nèi)存空間,可以看作是當前線程所執(zhí)行的字節(jié)碼的行號指示器。字節(jié)碼解釋器工作時就是通過改變這個計數(shù)器的值來選取下一條需要執(zhí)行的字節(jié)碼指令,分支、循環(huán)、跳轉(zhuǎn)、異常處理、線程恢復等基礎功能都需要依賴這個計數(shù)器完成。如果線程正在執(zhí)行一個Java方法,計數(shù)器記錄的是正在執(zhí)行的虛擬機字節(jié)碼指令的地址,如果正在執(zhí)行的是Native方法,這個計數(shù)器值為空。此內(nèi)存區(qū)域是唯一一個在Java虛擬機規(guī)范中沒有規(guī)定任何OutOfMemoryError情況的區(qū)域。

寫了這么多,感覺還是少一個例子。通過最簡單的一段代碼解釋一下,程序在運行時數(shù)據(jù)區(qū)個部分的變化情況。

public class Test{
  public static void main(String[] args){
   String name = "best.lei";
   sayHello(name);
  }
  public static void sayHello(String name){
   System.out.println("Hello " + name);
  }
}

通過編譯器將Test.java文件編譯為Test.class,利用javap -verbose Test.class對編譯后的字節(jié)碼進行分析,如下圖所示:

我們在看看運行時數(shù)據(jù)區(qū)的變化:

以上就是本文的全部內(nèi)容,希望本文的內(nèi)容對大家的學習或者工作能帶來一定的幫助,同時也希望多多支持腳本之家!

相關文章

  • 使用JMX監(jiān)控Zookeeper狀態(tài)Java API

    使用JMX監(jiān)控Zookeeper狀態(tài)Java API

    今天小編就為大家分享一篇關于使用JMX監(jiān)控Zookeeper狀態(tài)Java API,小編覺得內(nèi)容挺不錯的,現(xiàn)在分享給大家,具有很好的參考價值,需要的朋友一起跟隨小編來看看吧
    2019-03-03
  • Java泛型之上界下界通配符詳解

    Java泛型之上界下界通配符詳解

    這篇文章主要介紹了Java泛型之上界下界通配符詳解,學習使用泛型編程時,更令人困惑的一個方面是確定何時使用上限有界通配符以及何時使用下限有界通配符。本文提供一些設計代碼時要遵循的一些準則。,需要的朋友可以參考下
    2019-06-06
  • MybatisPlus關聯(lián)查詢的完美實現(xiàn)方案

    MybatisPlus關聯(lián)查詢的完美實現(xiàn)方案

    我們在項目開發(fā)的時候,難免會遇到連表查詢的操作,所以下面這篇文章主要給大家介紹了關于MybatisPlus關聯(lián)查詢的相關資料,文中通過實例代碼介紹的非常詳細,需要的朋友可以參考下
    2021-12-12
  • SpringBoot之控制器的返回值處理方式

    SpringBoot之控制器的返回值處理方式

    這篇文章主要介紹了SpringBoot之控制器的返回值處理方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2023-07-07
  • SkyWalking自定義鏈路追蹤實現(xiàn)步驟

    SkyWalking自定義鏈路追蹤實現(xiàn)步驟

    本文詳細介紹了如何使用SkyWalking進行自定義鏈路追蹤的步驟,包括POM文件依賴和邏輯業(yè)務代碼的編寫,文章最后推薦了腳本之家作為進一步學習的資源
    2024-02-02
  • 基于springcloud異步線程池、高并發(fā)請求feign的解決方案

    基于springcloud異步線程池、高并發(fā)請求feign的解決方案

    這篇文章主要介紹了基于springcloud異步線程池、高并發(fā)請求feign的解決方案,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2021-02-02
  • Java 數(shù)據(jù)結構與算法系列精講之哈希算法實現(xiàn)

    Java 數(shù)據(jù)結構與算法系列精講之哈希算法實現(xiàn)

    哈希表本質(zhì)是一種(key,value)結構,由此我們可以聯(lián)想到,能不能把哈希表的key映射成數(shù)組的索引index呢?如果這樣做的話那么查詢相當于直接查詢索引,查詢時間復雜度為O(1),其實這也正是當key為int型時的做法,將key通過某種做法映射成index,從而轉(zhuǎn)換成數(shù)組結構
    2022-02-02
  • Java實現(xiàn)PDF轉(zhuǎn)為線性PDF詳解

    Java實現(xiàn)PDF轉(zhuǎn)為線性PDF詳解

    線性化PDF文件是PDF文件的一種特殊格式,可以通過Internet更快地進行查看。本文將通過后端Java程序?qū)崿F(xiàn)將PDF文件轉(zhuǎn)為線性化PDF。感興趣的可以了解一下
    2021-12-12
  • Java上傳文件大小受限問題的解決方法

    Java上傳文件大小受限問題的解決方法

    這篇文章主要介紹了Java上傳文件大小受限怎么解決,本文給大家分享問題分析及解決方案,需要的朋友可以參考下
    2023-09-09
  • 使用spring?security?BCryptPasswordEncoder接入系統(tǒng)

    使用spring?security?BCryptPasswordEncoder接入系統(tǒng)

    這篇文章主要介紹了使用spring?security?BCryptPasswordEncoder接入系統(tǒng)方式,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2023-08-08

最新評論