自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

程序員從宏觀、微觀角度淺析JVM虛擬機!

云計算 虛擬化
本文將從宏觀及微觀角度來介紹類文件結(jié)構(gòu)、虛擬機加載類文件機制,類文件生命周期及字節(jié)碼加載引擎,更加立體的加深對虛擬機工作的認識。

 1.問題

  1. JAVA文本文件如何被翻譯成CLASS二進制文件?
  2. 如何理解CLASS文件的組成結(jié)構(gòu)?
  3. 虛擬機如何加載使用類文件的生命周期?
  4. 虛擬機系列診斷工具如何使用?
  5. 虛擬機內(nèi)存淘汰機制?
  6. 虛擬機指令集架構(gòu)?

2.關(guān)鍵詞

編譯,魔數(shù),常量池,字面量,數(shù)據(jù)表,堆棧,方法區(qū),程序計數(shù)器,內(nèi)存引用,內(nèi)存溢出,垃圾回收器,新生區(qū),***區(qū),指令集

3.全文概要

本文將從宏觀及微觀角度來介紹類文件結(jié)構(gòu)、虛擬機加載類文件機制,類文件生命周期及字節(jié)碼加載引擎,更加立體的加深對虛擬機工作的認識。

4.CLASS文件結(jié)構(gòu)分析

從我們學習JAVA語言的***天起,就執(zhí)行過JAVA/JAVAC命令。JAVAC就是把我們寫好的后綴為.java的文本文件編譯成后綴為.class的字節(jié)碼文件。上一章我們介紹代碼本質(zhì)的時候就了解到JAVA語言的語法元素。java文件我們可以通過文本編輯器打開,里面也是我們熟悉的java代碼,符合了java語言的語法規(guī)范。但是對于class里面的內(nèi)容,我們要陌生很多。上一章我們知道代碼通過編譯器翻譯成機器指令,那class文件會不會也是java虛擬機翻譯成的指令呢?

其實當java文件被編譯成class文件后,就跟java語言沒什么關(guān)系了。指令執(zhí)行引擎是JVM虛擬機,其他編程語言,比如Scala,Python等都可以編譯成class文件,然后放到JVM來執(zhí)行。這么說來,我們更加有必要探究class文件的本質(zhì)了。

4.1 CLASS文件示例

我們先從微觀的角度來介紹class文件的結(jié)構(gòu)。先寫一個簡單的java文本文件,然后編譯成class文件,來觀察class的結(jié)構(gòu)組成。

先定義一個接口文件,Add.java文件如下:

  1. package com.lzh.jvm; 
  2. public interface Add
  3.  int add(int i,int j); 
  4. 再寫一個接口的實現(xiàn)類AddImpl.java,這個基本包含我們?nèi)粘=?jīng)常使用的文件結(jié)構(gòu): 
  5. package com.lzh.jvm; 
  6. public class AddImpl implements Add
  7.  public static final int TOP = 100; 
  8.  private String point; 
  9.  public int add(int i,int j){ 
  10.  return i + j; 
  11.  } 

由于存在包名定義我們需要建好com/lzh/jvm的文件目錄,然后在當前目錄先后編譯com/lzh/jvm/Add.java文件和com/lzh/jvm/AddImpl.java文件。得到了Add.class文件和AddImpl.class文件。

Add.java二進制文件:

 

 

程序員從宏觀、微觀角度淺析JVM虛擬機!

 

 

Add.class二進制文件:

 

 

程序員從宏觀、微觀角度淺析JVM虛擬機!

 

 

AddImpl.java二進制文件:

 

 

程序員從宏觀、微觀角度淺析JVM虛擬機!

 

 

AddImpl.class二進制文件:

 

 

程序員從宏觀、微觀角度淺析JVM虛擬機!

 

 

以上四個圖是用WinHex二進制編輯工具打開的,左邊是文件的二進制編碼,右邊是ASCII標準編碼,所以只能表示英式鍵盤上的字符,出現(xiàn)中文的話則顯示亂碼。為了閱讀方便,工具展示的是16進制的格式,兩個16進制的編碼表示一個字節(jié)空間(8位)。

直觀上我們可以看出來java文件占用的存儲空間比class要少很多,這也符合我們上一章介紹的代碼翻譯過程。本質(zhì)上計算機并不認識java文件里面的內(nèi)容,java屬于高級語言,里面的語法更為接近人類的語言,但是對于計算機來說全難以理解。所以需要把java文件的內(nèi)容翻譯成jvm認識的文件格式。

 

高級語言高度抽象了語言元素,翻譯為機器指令則要花費更多的“口舌”來指導計算機一步步執(zhí)行代碼語句。下一節(jié)我們來解釋class文件的結(jié)構(gòu),從而理解jvm如何理解執(zhí)行class的內(nèi)容。

4.2 class文件結(jié)構(gòu)說明

本節(jié)我們將以上圖給的AddImpl.class為例子來介紹類的結(jié)構(gòu)。從結(jié)構(gòu)上來看,class文件只存放兩種類型數(shù)據(jù),分別為基礎(chǔ)字段和表。

  • 基礎(chǔ)字段:用于描述數(shù)字,引用,數(shù)值或字符串的無符號數(shù),類型為u1,u2,u4,u8表示占用字節(jié)數(shù)
  • 表:只有一行的可變列數(shù)的表結(jié)構(gòu),每個字段可以是基礎(chǔ)字段或其他表的索引

4.2.1 魔數(shù)

用于判斷文件類型,通常我們以文件后綴來判別文件類型,但是如果修改后綴就會導致安全問題。class以4個字節(jié)的空間作為開端,來標明class的類型,CA FE BA BE表示class類型的文件。

4.2.2 版本數(shù)

魔數(shù)后面緊接著4個字節(jié)表示jdk版本號。

  • 次版本號:前兩個字段0x0000
  • 主版本號:后兩個字段0x0035,轉(zhuǎn)換十進制為53,對應(yīng)jdk1.9

4.2.3 常量池

常量池顧名思義是用于存放字符串常量,字符串常量包含:

  • 字面量:字符串,常量
  • 引用符合:類/接口全限定名,字段/方法名稱和修飾符

我們知道class本質(zhì)是一些表的集合,同樣常量池也不例外,只不過存放在常量池位置的表有特定的類型,共有11種類型,如下表(圖片引用《深入理解Java虛擬機 JVM高級特性與***實踐 》):

 

 

程序員從宏觀、微觀角度淺析JVM虛擬機!

 

 

每個表的表結(jié)構(gòu)說明如下:

 

 

程序員從宏觀、微觀角度淺析JVM虛擬機!

 

 

這11種類型的表***個字段統(tǒng)一為標志字段tag,占用u1一個字節(jié),用于表示該表存放的數(shù)據(jù)類型。

首先進入常量池開始的兩個字節(jié)(u2)表示的是常量池的長度,也就是表的個數(shù)。

我們可以看到例子中常量池個數(shù)為0x0017,轉(zhuǎn)換為十進制為23,由于第0個表為保留索引,表示沒引用到任何字符串,所以實際表的索引是從1開始計算,也就是1~23共22個表。

我們先觀察AddImpl.class常量池,分析第1張表的表結(jié)構(gòu)。查表可知緊接著表個數(shù)后面的u1位置為0A,轉(zhuǎn)換為十進制為10,該表類型為CONSTANT_Methodref_info,觀察表結(jié)構(gòu)可知接下來的兩個u2位置屬于該表的字段,這兩個字段都是表索引類型,0x0003表示引用第3個表,0x0013表示引用第19個表。

 

然后該表結(jié)束緊接著是第2張表***個表,該表tag為07是CONSTANT_Class_info類型,第二個空間為u2的字段值為0x0014,引用第20個表。

接著分析第3張表,根據(jù)同樣的方法,一直可以把常量池的表結(jié)構(gòu)分析完。常量池的作用就是把源代碼所有文本數(shù)據(jù)都集中在常量池這個區(qū)間位置內(nèi),里面各個表之間相互引用,統(tǒng)一管理文本數(shù)據(jù)。由于表之間的引用,***文本數(shù)據(jù)都是存放在CONSTANT_Class_info表里面,而該表規(guī)定文本長度的字段length空間是u2類型,占用2個字節(jié),空間2的16次方,65536/1024=64K,所以java的變量或方法名大小不能超過64K。

4.2.4 訪問標志

修飾類或接口的限定標志

在常量池結(jié)束后緊接著2個字節(jié)的訪問標志,共32個標志位。

4.2.5 類/父類/接口索引集合

類索引、父類索引與接口索引集合:指向常量池的CONSTANT_Class_info表,再由CONSTANT_Class_info表里面的index指向特定CONSTANT_Utf8_info表的bytes字段的字面量。

4.5.6 字段表集合

字段表集合:

字段表結(jié)構(gòu)如下

數(shù)組用 [ 表示,字段表用來表示類里面所有變量(不包括方法里面的局部變量)

4.5.7 方法表集合

方法表集合:

方法表結(jié)構(gòu)如下

4.5.8 屬性表集合

屬性表集合

方法體里面的內(nèi)容編譯為Code屬性,code表結(jié)構(gòu)如下

Code,Exceptions,LineNumberTable,LocalVariableTable,SourceFile,ConstantValue,InnerClasses,Deprecated,Synthetic

class文件就像是一個產(chǎn)品的模具,把模具制造出來的過程就是把class加載到j(luò)vm內(nèi)存的過程,然后jvm再照著class模具的樣子印出對象來。重點在于模具的設(shè)計,其實模具被生產(chǎn)出來也是需要它本身有一套模具。這就是class嚴格的結(jié)構(gòu)規(guī)范,class文件結(jié)構(gòu)規(guī)范給出了各個方面的要求,只有按照這個要求造出來的模具才是可用的,才可以被用來制造產(chǎn)品,不然連產(chǎn)品線都上不去,就如同jvm判斷class不符合規(guī)范而拒絕加載。

5.類文件生命周期

類加載時機

類初始化的時機,大部分為被動初始化,用不到的時候都不會初始化。

類加載過程

  • 加載:全限定名檢索二進制字節(jié)流(不止class文件)->讀取至方法區(qū)->在堆上生成class對應(yīng)的對象
  • 驗證:文件格式驗證(符合class文件規(guī)范)->元數(shù)據(jù)驗證(語義分析)->字節(jié)碼驗證(方法體校驗)->符號引用驗證??梢杂?Xverify:none來跳過類加載驗證
  • 準備:類變量分配內(nèi)存設(shè)置初值,并未進行賦值操作
  • 解析:針對類接口,字段,方法的符合引用進行解析匹配。類解析,接口解析,字段解析,類方法解析,接口方法解析,
  • 初始化:執(zhí)行類構(gòu)造器

()方法,按源碼順序執(zhí)行所有static的語句。沒有靜態(tài)變量或者static語句的類將不會有()。

類加載器

啟動類加載器,擴展類加載器,應(yīng)用程序類加載器

類加載器采用雙親委派機制來讀取類文件,破壞雙親委派模型如:OSGI服務(wù)由自定義類加載器機制實現(xiàn)。每個OSGI模塊(Bundle)都有自己的加載器

6.虛擬機診斷工具

虛擬機性能監(jiān)控與故障處理工具,給一個系統(tǒng)定位問題的時候,知識,經(jīng)驗是基礎(chǔ),數(shù)據(jù)是依據(jù),工具就是處理數(shù)據(jù)的手段。

JDK的命令行工具

  • 虛擬機進程狀況工具:jps -lvm
  • 虛擬機統(tǒng)計信息監(jiān)視工具:jstat -gc pid interval count
  • java配置信息工具:jinfo -flag pid
  • java內(nèi)存映像工具:jmap -dump:format=b,file=java.bin pid

生成堆轉(zhuǎn)儲文件

  • 虛擬機堆轉(zhuǎn)儲快照分析工具:jhat file 分析堆轉(zhuǎn)儲文件,通過瀏覽器訪問分析文件
  • java堆棧跟蹤工具:jstack [ option ] vmid

用于生成虛擬機當前時刻的線程快照threaddump或者Javacore

JDK的可視化工具

  • jconsole
  • jvisualvm

7.虛擬機內(nèi)存淘汰機制

本節(jié)從宏觀的角度講解JVM內(nèi)存結(jié)構(gòu)、內(nèi)存分配運行策略,垃圾回收機制。

7.1虛擬機內(nèi)存分布

java內(nèi)存區(qū)域與內(nèi)存溢出

jvm內(nèi)存區(qū)域:方法區(qū),虛擬機棧,本地方法棧,堆,程序計數(shù)器;

  • 程序計數(shù)器:字節(jié)碼行號指示器,每個線程需要一個程序計數(shù)器
  • 虛擬機棧:方法執(zhí)行時創(chuàng)建棧幀(存儲局部變量,操作棧,動態(tài)鏈接,方法出口)編譯時期就能確定占用空間大小,線程請求的棧深度超過jvm運行深度時拋StackOverflowError,當jvm棧無法申請到空閑內(nèi)存時拋OutOfMemoryError,通過-Xss,-Xsx來配置初始內(nèi)存
  • 本地方法棧:執(zhí)行本地方法,如操作系統(tǒng)api接口
  • 堆:存放對象的空間,通過-Xmx,-Xms配置堆大小,當堆無法申請到內(nèi)存時拋OutOfMemoryError
  • 方法區(qū):存儲類數(shù)據(jù),常量,常量池,靜態(tài)變量,通過MaxPermSize參數(shù)配置
  • 對象訪問:初始化一個對象,其引用存放于棧幀,對象存放于堆內(nèi)存,對象包含屬性信息和該對象父類、接口等類型數(shù)據(jù)(該類型數(shù)據(jù)存儲在方法區(qū)空間,對象擁有類型數(shù)據(jù)的地址)

7.2內(nèi)存回收算法

內(nèi)存回收概述:

虛擬機棧、本地棧和程序計數(shù)器在編譯完畢后已經(jīng)可以確定所需內(nèi)存空間,程序執(zhí)行完畢后也會自動釋放所有內(nèi)存空間,所以不需要進行動態(tài)回收優(yōu)化。

jvm內(nèi)存調(diào)優(yōu)主要針對堆和方法區(qū)兩大區(qū)域的內(nèi)存。

引用:強Strong,軟sfot,弱weak,虛phantom,強引用不會回收,軟引用在內(nèi)存達到溢出邊界時回收,弱引用在每次回收周期時回收,虛引用專門被標記為回收對象。

內(nèi)存分配與回收策略

  • 對象優(yōu)先在Eden區(qū)分配:
  • 新生對象回收策略Minor GC(頻繁)
  • 老年代對象回收策略Full GC/Major GC(慢)
  • 大對象直接進入老年代:

超過3m的對象直接進入老年區(qū) -XX:PretenureSizeThreshold=3145728(3M)

  • 長期存貨對象進入老年區(qū):

Survivor區(qū)中的對象經(jīng)歷一次Minor GC年齡增加一歲,超過15歲進入老年區(qū)

-XX:MaxTenuringThreshold=15

  • 動態(tài)對象年齡判定:設(shè)置Survivor區(qū)對象占用一半空間以上的對象進入老年區(qū)

垃圾收集算法

標記-清除、復制、標記-整理、分代收集(新生用復制,老年用標記-整理)

7.3內(nèi)存收集器

  • serial收集器:單線程,主要用于client模式
  • ParNew收集器:多線程版的serial,主要用于server模式
  • Parallel Scavenge收集器:線程可控吞吐量(用戶代碼時間/用戶代碼時間+垃圾收集時間),自動調(diào)節(jié)吞吐量,用戶新生代內(nèi)存區(qū)
  • Serial Old收集器:老年版本serial
  • Parallel Old收集器:老年版本Parallel Scavenge
  • CMS(Concurrent Mark Sweep)收集器:停頓時間短,并發(fā)收集
  • G1收集器:分塊標記整理,不產(chǎn)生碎片

8.虛擬機指令集架構(gòu)(執(zhí)行引擎)

8.1虛擬機字節(jié)碼執(zhí)行引擎

運行時棧幀結(jié)構(gòu)

每個方法調(diào)用開始到執(zhí)行完成的過程,對應(yīng)這一個棧幀在虛擬機棧里面從入棧到出棧的過程。

  • 棧幀包含:局部變量表,操作數(shù)棧,動態(tài)連接,方法返回
  • 方法調(diào)用

方法調(diào)用不等于方法執(zhí)行,而且確定調(diào)用方法的版本。

  • 方法調(diào)用字節(jié)碼指令:invokestatic,invokespecial,invokevirtual,invokeinterface
  • 靜態(tài)分派:靜態(tài)類型,實際類型,編譯器重載時通過參數(shù)的靜態(tài)類型來確定方法的版本。(選方法)
  • 動態(tài)分派:invokevirtual指令把類方法符號引用解析到不同直接引用上,來確定棧頂?shù)膶嶋H對象(選對象)
  • 單分派:靜態(tài)多分派,相同指令有多個方法版本。
  • 多分派:動態(tài)單分派,方法接受者只能確定唯一一個。

基于棧的字節(jié)碼解釋

解釋執(zhí)行:

基于棧指令集與基于寄存器的指令集:

基于本地解釋器執(zhí)行過程

類加載 執(zhí)行子系統(tǒng)案例

tomcat類加載,OSGI熱插拔,字節(jié)碼生成技術(shù),動態(tài)代理,Retrotranslator

9.虛擬機實現(xiàn)機制進化過程

程序編譯與代碼優(yōu)化

早期編譯(編譯期)

  • javac編譯器:解析與符號表填充,注解處理,生成字節(jié)碼
  • java語法糖:語法糖有助于代碼開發(fā),但是編譯后就會解開糖衣,還原到基礎(chǔ)語法的class二進制文件

重載要求方法具備不同的特征簽名(不包括返回值),但是class文件中,只要描述不是完全一致的方法就可以共存,如:

  1. public String foo(List<String> arg){ 
  2.  final int var = 0; 
  3.  return ""
  4. public int foo(List<Integer> arg){ 
  5.  int var = 0; 
  6.  return 0; 

晚期編譯(運行期)

  • HotSpot虛擬機內(nèi)的即時編譯
  1. 解析模式 -Xint 
  2. 編譯模式 -Xcomp 
  3. 混合模式 Mixed mode 
  4. 分層編譯:解釋執(zhí)行 -> C1(Client Compiler)編譯 -> C2編譯(Server Compiler) 
  5. 觸發(fā)條件:基于采樣的熱點探測,基于計數(shù)器的熱點探測 

10.總結(jié)

由于JVM涉及內(nèi)容較深且廣,篇幅有限無法深入分析細節(jié)。本文從微觀方面分析了作為原材料的CLASS文件的結(jié)構(gòu),又從宏觀方面闡述了JVM是如何消化每一個進入的CLASS。JVM自定義了一套邏輯上的指令集,這也呼應(yīng)了之前我們介紹的計算機如何運行一文,現(xiàn)代計算機性能有了長足的發(fā)展,但是本質(zhì)上還是完備的諾依曼體系架構(gòu)。隨著量子計算的突飛猛進,相信未來的計算模型也會有革命性的突破。

責任編輯:武曉燕 來源: 今日頭條
相關(guān)推薦

2009-08-31 15:34:30

Java虛擬機

2016-11-04 10:30:17

微信小程序

2021-09-10 00:34:22

Java 線程啟動

2019-11-11 09:02:51

MySQL數(shù)據(jù)庫索引

2018-10-26 15:30:49

程序員MySQL數(shù)據(jù)庫

2010-09-17 15:12:57

JVMJava虛擬機

2015-04-20 09:50:58

程序員

2017-12-07 09:07:15

數(shù)據(jù)中心宏觀微觀

2015-04-14 11:15:18

程序員創(chuàng)業(yè)程序員談創(chuàng)業(yè)

2010-02-24 10:39:28

Python虛擬機

2020-05-08 16:55:48

Java虛擬機JVM

2011-06-22 13:35:55

JVM

2009-06-14 18:43:57

LinuxWindows對比

2010-09-25 16:12:45

JVM虛擬機

2020-02-22 13:42:03

在線教育網(wǎng)課教育

2010-09-25 15:13:40

JVMJava虛擬機

2012-04-23 10:08:42

JVM程序員

2021-03-10 07:52:58

虛擬機程序VMware

2009-06-19 15:38:43

Java虛擬機

2011-11-30 14:12:05

JavaJVM虛擬機
點贊
收藏

51CTO技術(shù)棧公眾號