精品欧美一区二区三区在线观看 _久久久久国色av免费观看性色_国产精品久久在线观看_亚洲第一综合网站_91精品又粗又猛又爽_小泽玛利亚一区二区免费_91亚洲精品国偷拍自产在线观看 _久久精品视频在线播放_美女精品久久久_欧美日韩国产成人在线

為了搞清楚類加載,竟然手?jǐn)]JVM!

云計(jì)算 虛擬化
當(dāng)學(xué)習(xí)一個(gè)新知識(shí)不知道從哪下手的時(shí)候,最有效的辦法是梳理這個(gè)知識(shí)結(jié)構(gòu)的脈絡(luò)信息,匯總出一整張的思維導(dǎo)出。接下來就是按照思維導(dǎo)圖的知識(shí)結(jié)構(gòu),一個(gè)個(gè)學(xué)習(xí)相應(yīng)的知識(shí)點(diǎn),并匯總記錄。

 [[361294]]

本文轉(zhuǎn)載自微信公眾號(hào)「bugstack蟲洞棧」,作者小傅哥 。轉(zhuǎn)載本文請(qǐng)聯(lián)系bugstack蟲洞棧公眾號(hào)。  

目錄

  • 一、前言
  • 二、面試題
  • 三、類加載過程描述
  • 四、寫個(gè)代碼加載下
    • 1. 案例工程
    • 2. 代碼講解
  • 五、解析字節(jié)碼文件
    • 1. 提取部分字節(jié)碼
    • 2. 解析魔數(shù)并校驗(yàn)
    • 3. 解析版本號(hào)信息
    • 4. 解析全部?jī)?nèi)容對(duì)照
  • 六、總結(jié)
  • 七、系列推薦

一、前言

學(xué)習(xí),不知道從哪下手?

當(dāng)學(xué)習(xí)一個(gè)新知識(shí)不知道從哪下手的時(shí)候,最有效的辦法是梳理這個(gè)知識(shí)結(jié)構(gòu)的脈絡(luò)信息,匯總出一整張的思維導(dǎo)出。接下來就是按照思維導(dǎo)圖的知識(shí)結(jié)構(gòu),一個(gè)個(gè)學(xué)習(xí)相應(yīng)的知識(shí)點(diǎn),并匯總記錄。

就像JVM的學(xué)習(xí),可以說它包括了非常多的內(nèi)容,也是一個(gè)龐大的知識(shí)體系。例如:類加載、加載器、生命周期、性能優(yōu)化、調(diào)優(yōu)參數(shù)、調(diào)優(yōu)工具、優(yōu)化方案、內(nèi)存區(qū)域、虛擬機(jī)棧、直接內(nèi)存、內(nèi)存溢出、元空間、垃圾回收、可達(dá)性分析、標(biāo)記清除、回收過程等等。如果沒有梳理的一頭扎進(jìn)去,東一榔頭西一棒子,很容易造成學(xué)習(xí)恐懼感。

如圖 24-1 是 JVM 知識(shí)框架梳理,后續(xù)我們會(huì)按照這個(gè)結(jié)構(gòu)陸續(xù)講解每一塊內(nèi)容。

圖 24-1 JVM 知識(shí)框架

二、面試題

謝飛機(jī),小記!,很多知識(shí)根本就是背背背,也沒法操作,難學(xué)!

「謝飛機(jī)」:大哥,你問我兩個(gè)JVM問題,我看看我自己還行不!

「面試官」:啊?嗯!往死了問還是?

「謝飛機(jī)」:就就就,都行!你看著來!

「面試官」:啊,那 JVM 加載過程都是什么步驟?

「謝飛機(jī)」:巴拉巴拉,加載、驗(yàn)證、準(zhǔn)備、解析、初始化、使用、卸載!

「面試官」:嗯,背的挺好!我懷疑你沒操作過! 那加載的時(shí)候,JVM 規(guī)范規(guī)定從第幾位開始是解析常量池,以及數(shù)據(jù)類型是如何定義的,u1、u2、u4,是怎么個(gè)玩意?

「謝飛機(jī)」:握草!算了,告訴我看啥吧!

三、類加載過程描述

圖 24-2 JVM 類加載過程

「JVM 類加載過程分為」,加載、鏈接、初始化、使用和卸載這四個(gè)階段,在鏈接中又包括:驗(yàn)證、準(zhǔn)備、解析。

  • 「加載」:Java 虛擬機(jī)規(guī)范對(duì) class 文件格式進(jìn)行了嚴(yán)格的規(guī)則,但對(duì)于從哪里加載 class 文件,卻非常自由。Java 虛擬機(jī)實(shí)現(xiàn)可以從文件系統(tǒng)讀取、從JAR(或ZIP)壓縮包中提取 class 文件。除此之外也可以通過網(wǎng)絡(luò)下載、數(shù)據(jù)庫加載,甚至是運(yùn)行時(shí)直接生成的 class 文件。
  • 「鏈接」:包括了三個(gè)階段;
    • 驗(yàn)證,確保被加載類的正確性,驗(yàn)證字節(jié)流是否符合 class 文件規(guī)范,例魔數(shù) 0xCAFEBABE,以及版本號(hào)等。
    • 準(zhǔn)備,為類的靜態(tài)變量分配內(nèi)存并設(shè)置變量初始值等
    • 解析,解析包括解析出常量池?cái)?shù)據(jù)和屬性表信息,這里會(huì)包括 ConstantPool 結(jié)構(gòu)體以及 AttributeInfo 接口等。
  • 「初始化」:類加載完成的最后一步就是初始化,目的就是為標(biāo)記常量值的字段賦值,以及執(zhí)行 方法的過程。JVM虛擬機(jī)通過鎖的方式確保 clinit 僅被執(zhí)行一次
  • 「使用」:程序代碼執(zhí)行使用階段。
  • 「卸載」:程序代碼退出、異常、結(jié)束等。

四、寫個(gè)代碼加載下

JVM 之所以不好掌握,主要是因?yàn)椴缓脤?shí)操。虛擬機(jī)是 C++ 寫的,很多 Java 程序員根本就不會(huì)去讀,或者讀不懂。那么,也就沒辦法實(shí)實(shí)在在的體會(huì)到,到底是怎么加載的,加載的時(shí)候都干了啥。只有看到代碼,我才覺得自己學(xué)會(huì)了!

所以,我們這里要手動(dòng)寫一下,JVM 虛擬機(jī)的部分代碼,也就是類加載的過程。通過 Java 代碼來實(shí)現(xiàn) Java 虛擬機(jī)的部分功能,讓開發(fā) Java 代碼的程序員更容易理解虛擬機(jī)的執(zhí)行過程。

1. 案例工程

  1. interview-24 
  2. ├── pom.xml 
  3. └── src 
  4.     └── main 
  5.     │    └── java 
  6.     │        └── org.itstack.interview.jvm 
  7.     │             ├── classpath 
  8.     │             │   ├── impl 
  9.     │             │   │   ├── CompositeEntry.java 
  10.     │             │   │   ├── DirEntry.java  
  11.     │             │   │   ├── WildcardEntry.java  
  12.     │             │   │   └── ZipEntry.java     
  13.     │             │   ├── Classpath.java 
  14.     │             │   └── Entry.java     
  15.     │             ├── Cmd.java 
  16.     │             └── Main.java 
  17.     └── test 
  18.          └── java 
  19.              └── org.itstack.interview.jvm.test 
  20.                  └── HelloWorld.java 

「以上」,工程結(jié)構(gòu)就是按照 JVM 虛擬機(jī)規(guī)范,使用 Java 代碼實(shí)現(xiàn) JVM 中加載 class 文件部分內(nèi)容。當(dāng)然這部分還不包括解析,因?yàn)榻馕霾糠值拇a非常龐大,我們先從把 .class 文件加載讀取開始了解。

2. 代碼講解

2.1 定義類路徑接口(Entry)

  1. public interface Entry { 
  2.  
  3.     byte[] readClass(String className) throws IOException; 
  4.      
  5.     static Entry create(String path) { 
  6.         //File.pathSeparator;路徑分隔符(win\linux) 
  7.         if (path.contains(File.pathSeparator)) { 
  8.             return new CompositeEntry(path); 
  9.         } 
  10.         if (path.endsWith("*")) { 
  11.             return new WildcardEntry(path); 
  12.         } 
  13.         if (path.endsWith(".jar") || path.endsWith(".JAR") || 
  14.                 path.endsWith(".zip") || path.endsWith(".ZIP")) { 
  15.             return new ZipEntry(path); 
  16.         } 
  17.         return new DirEntry(path); 
  18.     } 
  • 接口中提供了接口方法 readClass 和靜態(tài)方法 create(String path)。
  • jdk1.8 是可以在接口中編寫靜態(tài)方法的,在設(shè)計(jì)上屬于補(bǔ)全了抽象類的類似功能。這個(gè)靜態(tài)方法主要是按照不同的路徑地址類型,提供不同的解析方法。包括:CompositeEntry、WildcardEntry、ZipEntry、DirEntry,這四種。接下來分別看每一種的具體實(shí)現(xiàn)

2.2 目錄形式路徑(DirEntry)

  1. public class DirEntry implements Entry { 
  2.  
  3.     private Path absolutePath; 
  4.  
  5.     public DirEntry(String path){ 
  6.         //獲取絕對(duì)路徑 
  7.         this.absolutePath = Paths.get(path).toAbsolutePath(); 
  8.     } 
  9.  
  10.     @Override 
  11.     public byte[] readClass(String className) throws IOException { 
  12.         return Files.readAllBytes(absolutePath.resolve(className)); 
  13.     } 
  14.  
  15.     @Override 
  16.     public String toString() { 
  17.         return this.absolutePath.toString(); 
  18.     } 

目錄形式的通過讀取絕對(duì)路徑下的文件,通過 Files.readAllBytes 方式獲取字節(jié)碼。

2.3 壓縮包形式路徑(ZipEntry)

  1. public class ZipEntry implements Entry { 
  2.  
  3.     private Path absolutePath; 
  4.  
  5.     public ZipEntry(String path) { 
  6.         //獲取絕對(duì)路徑 
  7.         this.absolutePath = Paths.get(path).toAbsolutePath(); 
  8.     } 
  9.  
  10.     @Override 
  11.     public byte[] readClass(String className) throws IOException { 
  12.         try (FileSystem zipFs = FileSystems.newFileSystem(absolutePath, null)) { 
  13.             return Files.readAllBytes(zipFs.getPath(className)); 
  14.         } 
  15.     } 
  16.  
  17.     @Override 
  18.     public String toString() { 
  19.         return this.absolutePath.toString(); 
  20.     } 
  21.  
  • 其實(shí)壓縮包形式與目錄形式,只有在文件讀取上有包裝差別而已。FileSystems.newFileSystem

2.4 混合形式路徑(CompositeEntry)

  1. public class CompositeEntry implements Entry { 
  2.  
  3.     private final List<Entry> entryList = new ArrayList<>(); 
  4.  
  5.     public CompositeEntry(String pathList) { 
  6.         String[] paths = pathList.split(File.pathSeparator); 
  7.         for (String path : paths) { 
  8.             entryList.add(Entry.create(path)); 
  9.         } 
  10.     } 
  11.  
  12.     @Override 
  13.     public byte[] readClass(String className) throws IOException { 
  14.         for (Entry entry : entryList) { 
  15.             try { 
  16.                 return entry.readClass(className); 
  17.             } catch (Exception ignored) { 
  18.                 //ignored 
  19.             } 
  20.         } 
  21.         throw new IOException("class not found " + className); 
  22.     } 
  23.  
  24.  
  25.     @Override 
  26.     public String toString() { 
  27.         String[] strs = new String[entryList.size()]; 
  28.         for (int i = 0; i < entryList.size(); i++) { 
  29.             strs[i] = entryList.get(i).toString(); 
  30.         } 
  31.         return String.join(File.pathSeparator, strs); 
  32.     } 
  33.      
  • File.pathSeparator,是一個(gè)分隔符屬性,win/linux 有不同的類型,所以使用這個(gè)方法進(jìn)行分割路徑。
  • 分割后的路徑裝到 List 集合中,這個(gè)過程屬于拆分路徑。

2.5 通配符類型路徑(WildcardEntry)

  1. public class WildcardEntry extends CompositeEntry { 
  2.  
  3.     public WildcardEntry(String path) { 
  4.         super(toPathList(path)); 
  5.     } 
  6.  
  7.     private static String toPathList(String wildcardPath) { 
  8.         String baseDir = wildcardPath.replace("*"""); // remove * 
  9.         try { 
  10.             return Files.walk(Paths.get(baseDir)) 
  11.                     .filter(Files::isRegularFile) 
  12.                     .map(Path::toString) 
  13.                     .filter(p -> p.endsWith(".jar") || p.endsWith(".JAR")) 
  14.                     .collect(Collectors.joining(File.pathSeparator)); 
  15.         } catch (IOException e) { 
  16.             return ""
  17.         } 
  18.     } 
  19.  
  • 這個(gè)類屬于混合形式路徑處理類的子類,唯一提供的方法就是把類路徑解析出來。

2.6 類路徑解析(Classpath)

啟動(dòng)類路徑、擴(kuò)展類路徑、用戶類路徑,熟悉嗎?是不經(jīng)常看到這幾句話,那么時(shí)候怎么實(shí)現(xiàn)的呢?

有了上面我們做的一些基礎(chǔ)類的工作,接下來就是類解析的實(shí)際調(diào)用過程。代碼如下:

  1. public class Classpath { 
  2.  
  3.     private Entry bootstrapClasspath;  //啟動(dòng)類路徑 
  4.     private Entry extensionClasspath;  //擴(kuò)展類路徑 
  5.     private Entry userClasspath;       //用戶類路徑 
  6.  
  7.     public Classpath(String jreOption, String cpOption) { 
  8.         //啟動(dòng)類&擴(kuò)展類 "C:\Program Files\Java\jdk1.8.0_161\jre" 
  9.         bootstrapAndExtensionClasspath(jreOption); 
  10.         //用戶類 F:\..\org\itstack\demo\test\HelloWorld 
  11.         parseUserClasspath(cpOption); 
  12.     } 
  13.  
  14.     private void bootstrapAndExtensionClasspath(String jreOption) { 
  15.          
  16.         String jreDir = getJreDir(jreOption); 
  17.  
  18.         //..jre/lib/* 
  19.         String jreLibPath = Paths.get(jreDir, "lib") + File.separator + "*"
  20.         bootstrapClasspath = new WildcardEntry(jreLibPath); 
  21.  
  22.         //..jre/lib/ext/* 
  23.         String jreExtPath = Paths.get(jreDir, "lib""ext") + File.separator + "*"
  24.         extensionClasspath = new WildcardEntry(jreExtPath); 
  25.  
  26.     } 
  27.  
  28.     private static String getJreDir(String jreOption) { 
  29.         if (jreOption != null && Files.exists(Paths.get(jreOption))) { 
  30.             return jreOption; 
  31.         } 
  32.         if (Files.exists(Paths.get("./jre"))) { 
  33.             return "./jre"
  34.         } 
  35.         String jh = System.getenv("JAVA_HOME"); 
  36.         if (jh != null) { 
  37.             return Paths.get(jh, "jre").toString(); 
  38.         } 
  39.         throw new RuntimeException("Can not find JRE folder!"); 
  40.     } 
  41.  
  42.     private void parseUserClasspath(String cpOption) { 
  43.         if (cpOption == null) { 
  44.             cpOption = "."
  45.         } 
  46.         userClasspath = Entry.create(cpOption); 
  47.     } 
  48.  
  49.     public byte[] readClass(String className) throws Exception { 
  50.         className = className + ".class"
  51.  
  52.         //[readClass]啟動(dòng)類路徑 
  53.         try { 
  54.             return bootstrapClasspath.readClass(className); 
  55.         } catch (Exception ignored) { 
  56.             //ignored 
  57.         } 
  58.  
  59.         //[readClass]擴(kuò)展類路徑 
  60.         try { 
  61.             return extensionClasspath.readClass(className); 
  62.         } catch (Exception ignored) { 
  63.             //ignored 
  64.         } 
  65.  
  66.         //[readClass]用戶類路徑 
  67.         return userClasspath.readClass(className); 
  68.     } 
  69.  
  • 啟動(dòng)類路徑,bootstrapClasspath.readClass(className);
  • 擴(kuò)展類路徑,extensionClasspath.readClass(className);
  • 用戶類路徑,userClasspath.readClass(className);
  • 這回就看到它們具體在哪使用了吧!有了具體的代碼也就方便理解了

2.7 加載類測(cè)試驗(yàn)證

  1. private static void startJVM(Cmd cmd) { 
  2.     Classpath cp = new Classpath(cmd.jre, cmd.classpath); 
  3.     System.out.printf("classpath:%s class:%s args:%s\n", cp, cmd.getMainClass(), cmd.getAppArgs()); 
  4.     //獲取className 
  5.     String className = cmd.getMainClass().replace(".""/"); 
  6.     try { 
  7.         byte[] classData = cp.readClass(className); 
  8.         System.out.println(Arrays.toString(classData)); 
  9.     } catch (Exception e) { 
  10.         System.out.println("Could not find or load main class " + cmd.getMainClass()); 
  11.         e.printStackTrace(); 
  12.     } 

這段就是使用 Classpath 類進(jìn)行類路徑加載,這里我們測(cè)試加載 java.lang.String 類。你可以加載其他的類,或者自己寫的類

  • 配置IDEA,program arguments 參數(shù):-Xjre "C:\Program Files\Java\jdk1.8.0_161\jre" java.lang.String
  • 另外這里讀取出的 class 文件信息,打印的是 byte 類型信息。

「測(cè)試結(jié)果」

  1. [-54, -2, -70, -66, 0, 0, 0, 52, 2, 28, 3, 0, 0, -40, 0, 3, 0, 0, -37, -1, 3, 0, 0, -33, -1, 3, 0, 1, 0, 0, 8, 0, 15, 8, 0, 61, 8, 0, 85, 8, 0, 88, 8, 0, 89, 8, 0, 112, 8, 0, -81, 8, 0, -75, 8, 0, -47, 8, 0, -45, 1, 0, 0, 1, 0, 3, 40, 41, 73, 1, 0, 20, 40, 41, 76, 106, 97, 118, 97, 47, 108, 97, 110, 103, 47, 79, 98, 106, 101, 99, 116, 59, 1, 0, 20, 40, 41, 76, 106, 97, 118, 97, 47, 108, 97, 110, 103, 47, 83, 116, 114, 105, 110, 103, 59, 1, 0, 3, 40, 41, 86, 1, 0, 3, 40, 41, 90, 1, 0, 4, 40, 41, 91, ...] 

這塊部分截取的程序運(yùn)行打印結(jié)果,就是讀取的 class 文件信息,只不過暫時(shí)還不能看出什么。接下來我們?cè)侔阉g過來!

五、解析字節(jié)碼文件

JVM 在把 class 文件加載完成后,接下來就進(jìn)入鏈接的過程,這個(gè)過程包括了內(nèi)容的校驗(yàn)、準(zhǔn)備和解析,其實(shí)就是把 byte 類型 class 翻譯過來,做相應(yīng)的操作。

整個(gè)這個(gè)過程內(nèi)容相對(duì)較多,這里只做部分邏輯的實(shí)現(xiàn)和講解。如果讀者感興趣可以閱讀小傅哥的《用Java實(shí)現(xiàn)JVM》專欄。

1. 提取部分字節(jié)碼

  1. //取部分字節(jié)碼:java.lang.String 
  2. private static byte[] classData = { 
  3.         -54, -2, -70, -66, 0, 0, 0, 52, 2, 26, 3, 0, 0, -40, 0, 3, 0, 0, -37, -1, 3, 0, 0, -33, -1, 3, 0, 1, 0, 0, 8, 0, 
  4.         59, 8, 0, 83, 8, 0, 86, 8, 0, 87, 8, 0, 110, 8, 0, -83, 8, 0, -77, 8, 0, -49, 8, 0, -47, 1, 0, 3, 40, 41, 73, 1, 
  5.         0, 20, 40, 41, 76, 106, 97, 118, 97, 47, 108, 97, 110, 103, 47, 79, 98, 106, 101, 99, 116, 59, 1, 0, 20, 40, 41, 
  6.         76, 106, 97, 118, 97, 47, 108, 97, 110, 103, 47, 83, 116, 114, 105, 110, 103, 59, 1, 0, 3, 40, 41, 86, 1, 0, 3, 
  7.         40, 41, 90, 1, 0, 4, 40, 41, 91, 66, 1, 0, 4, 40, 41, 91, 67, 1, 0, 4, 40, 67, 41, 67, 1, 0, 21, 40, 68, 41, 76, 
  8.         106, 97, 118, 97, 47, 108, 97, 110, 103, 47, 83, 116, 114, 105, 110, 103, 59, 1, 0, 4, 40, 73, 41, 67, 1, 0, 4}; 

java.lang.String 解析出來的字節(jié)碼內(nèi)容較多,當(dāng)然包括的內(nèi)容也多,比如魔數(shù)、版本、類、常量、方法等等。所以我們這里只截取部分進(jìn)行進(jìn)行解析。

2. 解析魔數(shù)并校驗(yàn)

很多文件格式都會(huì)規(guī)定滿足該格式的文件必須以某幾個(gè)固定字節(jié)開頭,這幾個(gè)字節(jié)主要起到標(biāo)識(shí)作用,叫作魔數(shù)(magic number)。

例如;

  • PDF文件以4字節(jié)“%PDF”(0x25、0x50、0x44、0x46)開頭,
  • ZIP文件以2字節(jié)“PK”(0x50、0x4B)開頭
  • class文件以4字節(jié)“0xCAFEBABE”開頭
  1. private static void readAndCheckMagic() { 
  2.     System.out.println("\r\n------------ 校驗(yàn)?zāi)?shù) ------------"); 
  3.     //從class字節(jié)碼中讀取前四位 
  4.     byte[] magic_byte = new byte[4]; 
  5.     System.arraycopy(classData, 0, magic_byte, 0, 4); 
  6.      
  7.     //將4位byte字節(jié)轉(zhuǎn)成16進(jìn)制字符串 
  8.     String magic_hex_str = new BigInteger(1, magic_byte).toString(16); 
  9.     System.out.println("magic_hex_str:" + magic_hex_str); 
  10.      
  11.     //byte_magic_str 是16進(jìn)制的字符串,cafebabe,因?yàn)閖ava中沒有無符號(hào)整型,所以如果想要無符號(hào)只能放到更高位中 
  12.     long magic_unsigned_int32 = Long.parseLong(magic_hex_str, 16); 
  13.     System.out.println("magic_unsigned_int32:" + magic_unsigned_int32); 
  14.      
  15.     //魔數(shù)比對(duì),一種通過字符串比對(duì),另外一種使用假設(shè)的無符號(hào)16進(jìn)制比較。如果使用無符號(hào)比較需要將0xCAFEBABE & 0x0FFFFFFFFL與運(yùn)算 
  16.     System.out.println("0xCAFEBABE & 0x0FFFFFFFFL:" + (0xCAFEBABE & 0x0FFFFFFFFL)); 
  17.      
  18.     if (magic_unsigned_int32 == (0xCAFEBABE & 0x0FFFFFFFFL)) { 
  19.         System.out.println("class字節(jié)碼魔數(shù)無符號(hào)16進(jìn)制數(shù)值一致校驗(yàn)通過"); 
  20.     } else { 
  21.         System.out.println("class字節(jié)碼魔數(shù)無符號(hào)16進(jìn)制數(shù)值一致校驗(yàn)拒絕"); 
  22.     } 
  • 讀取字節(jié)碼中的前四位,-54, -2, -70, -66,將這四位轉(zhuǎn)換為16進(jìn)制。
  • 因?yàn)?java 中是沒有無符號(hào)整型的,所以只能用更高位存放。
  • 解析后就是魔數(shù)的對(duì)比,看是否與 CAFEBABE 一致。

「測(cè)試結(jié)果」

  1. ------------ 校驗(yàn)?zāi)?shù) ------------ 
  2. magic_hex_str:cafebabe 
  3. magic_unsigned_int32:3405691582 
  4. 0xCAFEBABE & 0x0FFFFFFFFL:3405691582 
  5. class字節(jié)碼魔數(shù)無符號(hào)16進(jìn)制數(shù)值一致校驗(yàn)通過 

3. 解析版本號(hào)信息

剛才我們已經(jīng)讀取了4位魔數(shù)信息,接下來再讀取2位,是版本信息。

魔數(shù)之后是class文件的次版本號(hào)和主版本號(hào),都是u2類型。假設(shè)某class文件的主版本號(hào)是M,次版本號(hào)是m,那么完整的版本號(hào)可以表示成“M.m”的形式。次版本號(hào)只在J2SE 1.2之前用過,從1.2開始基本上就沒有什么用了(都是0)。主版本號(hào)在J2SE 1.2之前是45,從1.2開始,每次有大版本的Java版本發(fā)布,都會(huì)加1{45、46、47、48、49、50、51、52}

  1. private static void readAndCheckVersion() { 
  2.     System.out.println("\r\n------------ 校驗(yàn)版本號(hào) ------------"); 
  3.     //從class字節(jié)碼第4位開始讀取,讀取2位 
  4.     byte[] minor_byte = new byte[2]; 
  5.     System.arraycopy(classData, 4, minor_byte, 0, 2); 
  6.      
  7.     //將2位byte字節(jié)轉(zhuǎn)成16進(jìn)制字符串 
  8.     String minor_hex_str = new BigInteger(1, minor_byte).toString(16); 
  9.     System.out.println("minor_hex_str:" + minor_hex_str); 
  10.      
  11.     //minor_unsigned_int32 轉(zhuǎn)成無符號(hào)16進(jìn)制 
  12.     int minor_unsigned_int32 = Integer.parseInt(minor_hex_str, 16); 
  13.     System.out.println("minor_unsigned_int32:" + minor_unsigned_int32); 
  14.      
  15.     //從class字節(jié)碼第6位開始讀取,讀取2位 
  16.     byte[] major_byte = new byte[2]; 
  17.     System.arraycopy(classData, 6, major_byte, 0, 2); 
  18.      
  19.     //將2位byte字節(jié)轉(zhuǎn)成16進(jìn)制字符串 
  20.     String major_hex_str = new BigInteger(1, major_byte).toString(16); 
  21.     System.out.println("major_hex_str:" + major_hex_str); 
  22.      
  23.     //major_unsigned_int32 轉(zhuǎn)成無符號(hào)16進(jìn)制 
  24.     int major_unsigned_int32 = Integer.parseInt(major_hex_str, 16); 
  25.     System.out.println("major_unsigned_int32:" + major_unsigned_int32); 
  26.     System.out.println("版本號(hào):" + major_unsigned_int32 + "." + minor_unsigned_int32); 
  • 這里有一個(gè)小技巧,class 文件解析出來是一整片的內(nèi)容,JVM 需要按照虛擬機(jī)規(guī)范,一段一段的解析出所有的信息。
  • 同樣這里我們需要把2位byte轉(zhuǎn)換為16進(jìn)制信息,并繼續(xù)從第6位繼續(xù)讀取2位信息。組合出來的才是版本信息。

「測(cè)試結(jié)果」

  1. ------------ 校驗(yàn)版本號(hào) ------------ 
  2. minor_hex_str:0 
  3. minor_unsigned_int32:0 
  4. major_hex_str:34 
  5. major_unsigned_int32:52 
  6. 版本號(hào):52.0 

4. 解析全部?jī)?nèi)容對(duì)照

按照 JVM 的加載過程,其實(shí)遠(yuǎn)不止魔數(shù)和版本號(hào)信息,還有很多其他內(nèi)容,這里我們可以把測(cè)試結(jié)果展示出來,方便大家有一個(gè)學(xué)習(xí)結(jié)果的比對(duì)印象。

  1. classpath:org.itstack.demo.jvm.classpath.Classpath@4bf558aa class:java.lang.String args:null 
  2. version: 52.0 
  3. constants count:540 
  4. access flags:0x31 
  5. this class:java/lang/String 
  6. super class:java/lang/Object 
  7. interfaces:[java/io/Serializable, java/lang/Comparable, java/lang/CharSequence] 
  8. fields count:5 
  9. value    [C 
  10. hash    I 
  11. serialVersionUID    J 
  12. serialPersistentFields    [Ljava/io/ObjectStreamField; 
  13. CASE_INSENSITIVE_ORDER    Ljava/util/Comparator; 
  14. methods count: 94 
  15. <init>    ()V 
  16. <init>    (Ljava/lang/String;)V 
  17. <init>    ([C)V 
  18. <init>    ([CII)V 
  19. <init>    ([III)V 
  20. <init>    ([BIII)V 
  21. <init>    ([BI)V 
  22. checkBounds    ([BII)V 
  23. <init>    ([BIILjava/lang/String;)V 
  24. <init>    ([BIILjava/nio/charset/Charset;)V 
  25. <init>    ([BLjava/lang/String;)V 
  26. <init>    ([BLjava/nio/charset/Charset;)V 
  27. <init>    ([BII)V 
  28. <init>    ([B)V 
  29. <init>    (Ljava/lang/StringBuffer;)V 
  30. <init>    (Ljava/lang/StringBuilder;)V 
  31. <init>    ([CZ)V 
  32. length    ()I 
  33. isEmpty    ()Z 
  34. charAt    (I)C 
  35. codePointAt    (I)I 
  36. codePointBefore    (I)I 
  37. codePointCount    (II)I 
  38. offsetByCodePoints    (II)I 
  39. getChars    ([CI)V 
  40. getChars    (II[CI)V 
  41. getBytes    (II[BI)V 
  42. getBytes    (Ljava/lang/String;)[B 
  43. getBytes    (Ljava/nio/charset/Charset;)[B 
  44. getBytes    ()[B 
  45. equals    (Ljava/lang/Object;)Z 
  46. contentEquals    (Ljava/lang/StringBuffer;)Z 
  47. nonSyncContentEquals    (Ljava/lang/AbstractStringBuilder;)Z 
  48. contentEquals    (Ljava/lang/CharSequence;)Z 
  49. equalsIgnoreCase    (Ljava/lang/String;)Z 
  50. compareTo    (Ljava/lang/String;)I 
  51. compareToIgnoreCase    (Ljava/lang/String;)I 
  52. regionMatches    (ILjava/lang/String;II)Z 
  53. regionMatches    (ZILjava/lang/String;II)Z 
  54. startsWith    (Ljava/lang/String;I)Z 
  55. startsWith    (Ljava/lang/String;)Z 
  56. endsWith    (Ljava/lang/String;)Z 
  57. hashCode    ()I 
  58. indexOf    (I)I 
  59. indexOf    (II)I 
  60. indexOfSupplementary    (II)I 
  61. lastIndexOf    (I)I 
  62. lastIndexOf    (II)I 
  63. lastIndexOfSupplementary    (II)I 
  64. indexOf    (Ljava/lang/String;)I 
  65. indexOf    (Ljava/lang/String;I)I 
  66. indexOf    ([CIILjava/lang/String;I)I 
  67. indexOf    ([CII[CIII)I 
  68. lastIndexOf    (Ljava/lang/String;)I 
  69. lastIndexOf    (Ljava/lang/String;I)I 
  70. lastIndexOf    ([CIILjava/lang/String;I)I 
  71. lastIndexOf    ([CII[CIII)I 
  72. substring    (I)Ljava/lang/String; 
  73. substring    (II)Ljava/lang/String; 
  74. subSequence    (II)Ljava/lang/CharSequence; 
  75. concat    (Ljava/lang/String;)Ljava/lang/String; 
  76. replace    (CC)Ljava/lang/String; 
  77. matches    (Ljava/lang/String;)Z 
  78. contains    (Ljava/lang/CharSequence;)Z 
  79. replaceFirst    (Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String; 
  80. replaceAll    (Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String; 
  81. replace    (Ljava/lang/CharSequence;Ljava/lang/CharSequence;)Ljava/lang/String; 
  82. split    (Ljava/lang/String;I)[Ljava/lang/String; 
  83. split    (Ljava/lang/String;)[Ljava/lang/String; 
  84. join    (Ljava/lang/CharSequence;[Ljava/lang/CharSequence;)Ljava/lang/String; 
  85. join    (Ljava/lang/CharSequence;Ljava/lang/Iterable;)Ljava/lang/String; 
  86. toLowerCase    (Ljava/util/Locale;)Ljava/lang/String; 
  87. toLowerCase    ()Ljava/lang/String; 
  88. toUpperCase    (Ljava/util/Locale;)Ljava/lang/String; 
  89. toUpperCase    ()Ljava/lang/String; 
  90. trim    ()Ljava/lang/String; 
  91. toString    ()Ljava/lang/String; 
  92. toCharArray    ()[C 
  93. format    (Ljava/lang/String;[Ljava/lang/Object;)Ljava/lang/String; 
  94. format    (Ljava/util/Locale;Ljava/lang/String;[Ljava/lang/Object;)Ljava/lang/String; 
  95. valueOf    (Ljava/lang/Object;)Ljava/lang/String; 
  96. valueOf    ([C)Ljava/lang/String; 
  97. valueOf    ([CII)Ljava/lang/String; 
  98. copyValueOf    ([CII)Ljava/lang/String; 
  99. copyValueOf    ([C)Ljava/lang/String; 
  100. valueOf    (Z)Ljava/lang/String; 
  101. valueOf    (C)Ljava/lang/String; 
  102. valueOf    (I)Ljava/lang/String; 
  103. valueOf    (J)Ljava/lang/String; 
  104. valueOf    (F)Ljava/lang/String; 
  105. valueOf    (D)Ljava/lang/String; 
  106. intern    ()Ljava/lang/String; 
  107. compareTo    (Ljava/lang/Object;)I 
  108. <clinit>    ()V 
  109.  
  110. Process finished with exit code 0 

如果大家對(duì)這部分驗(yàn)證、準(zhǔn)備、解析,的實(shí)現(xiàn)過程感興趣,可以參照這部分用Java實(shí)現(xiàn)的JVM源碼:https://github.com/fuzhengwei/itstack-demo-jvm

六、總結(jié)

學(xué)習(xí) JVM 最大的問題是不好實(shí)踐,所以本文以案例實(shí)操的方式,學(xué)習(xí) JVM 的加載解析過程。也讓更多的對(duì) JVM 感興趣的研發(fā),能更好的接觸到 JVM 并深入的學(xué)習(xí)。

有了以上這段代碼,大家可以參照 JVM 虛擬機(jī)規(guī)范,在調(diào)試Java版本的JVM,這樣就可以非常容易理解整個(gè)JVM的加載過程,都做了什么。

如果大家需要文章中一些原圖 xmind 或者源碼,可以添加作者小傅哥(fustack),或者關(guān)注公眾號(hào):bugstack蟲洞棧進(jìn)行獲取。好了,本章節(jié)就扯到這,后續(xù)還有很多努力,持續(xù)原創(chuàng),感謝大家的支持!

 

責(zé)任編輯:武曉燕 來源: bugstack蟲洞棧
相關(guān)推薦

2021-09-01 09:32:40

工具

2020-11-16 08:37:16

MariaDB性能優(yōu)化

2011-06-22 09:37:03

桌面虛擬化存儲(chǔ)

2021-05-06 10:25:37

蘋果手機(jī)二手

2017-08-15 08:27:48

云備份問題恢復(fù)

2020-12-16 11:09:27

JavaScript語言開發(fā)

2021-01-19 06:43:10

Netty框架網(wǎng)絡(luò)技術(shù)

2018-06-26 14:42:10

StringJava數(shù)據(jù)

2018-06-20 10:43:58

云端霧端霧計(jì)算

2015-10-12 10:01:26

AndroidWindows應(yīng)用Windows 10

2020-04-28 17:26:04

監(jiān)督學(xué)習(xí)無監(jiān)督學(xué)習(xí)機(jī)器學(xué)習(xí)

2022-11-16 14:02:44

2011-03-07 17:44:59

中小企業(yè)實(shí)施虛擬化

2020-04-11 11:21:22

留存分析模型分析

2022-08-08 08:48:15

Go版本偽版本

2022-10-24 00:33:59

MySQL全局鎖行級(jí)鎖

2023-02-17 14:40:08

MySQLSQL優(yōu)化

2025-06-24 09:16:48

2023-06-26 11:59:52

標(biāo)簽質(zhì)量梳理

2021-05-31 07:22:46

ORM框架程序
點(diǎn)贊
收藏

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

一区在线中文字幕| 亚洲成人网在线播放| 亚洲AV无码精品自拍| 欧美日韩国产色综合一二三四| 欧美精品v国产精品v日韩精品| 在线成人av电影| 丰满人妻一区二区三区免费| 欧美另类女人| 在线亚洲国产精品网| www亚洲成人| 精精国产xxxx视频在线中文版| av成人老司机| 国产日韩欧美日韩| 日本特黄一级片| 美女少妇全过程你懂的久久| 欧美三片在线视频观看| 成人短视频在线观看免费| 头脑特工队2免费完整版在线观看 头脑特工队2在线播放 | 99久久99久久精品国产片桃花| 欧美一级理论片| 99免费视频观看| 欧美aaaaaaa| 国产精品灌醉下药二区| 国产精品免费区二区三区观看| 国产情侣小视频| 尤物网精品视频| 久久久精品国产| 色一情一交一乱一区二区三区| 狂野欧美xxxx韩国少妇| 一本一道久久a久久精品综合蜜臀| 欧美aaa在线观看| eeuss影院www在线观看| 成人国产电影网| 亚洲中国色老太| 亚洲怡红院av| 男女性色大片免费观看一区二区| 欧美精品video| 国产美女网站视频| 无码人妻精品一区二区中文| 青青草在线播放| 成人一区二区三区| 亚洲一区二区中文| 国产一区二区在线不卡| 巨乳诱惑日韩免费av| 欧美国产日韩一区二区三区| 免费毛片视频网站| 亚洲自拍电影| 亚洲精品一区二区三区婷婷月| 女同性αv亚洲女同志| 成人18视频在线观看| 欧美午夜精品在线| 黄色一级片播放| av免费不卡国产观看| 亚洲精品日韩一| 国产日韩欧美大片| 一区二区三区伦理| 亚洲黄色小视频| 蜜桃视频一区二区在线观看| 91社区在线观看播放| 欧美国产成人精品| 亚洲一区二区三区在线观看视频| 国产片在线观看| 国产女主播一区| 影音先锋欧美在线| av免费在线网站| 夜夜操天天操亚洲| 欧美成人免费在线观看视频| 新版中文在线官网| 亚洲成精国产精品女| 中文字幕人妻熟女人妻洋洋| 日韩精品黄色| 一区二区三区精密机械公司| 伊人久久青草| bl在线肉h视频大尺度| 无码av免费一区二区三区试看| 高清欧美精品xxxxx| 97蜜桃久久| 欧美日韩在线观看视频| 久久精品99国产| 免费视频观看成人| 精品国产青草久久久久福利| 久草福利在线观看| 全球av集中精品导航福利| 国产午夜精品久久久| 亚洲精品色午夜无码专区日韩| 精品国产美女| 欧美孕妇性xx| youjizz.com日本| 欧美变态网站| 亚洲天堂网站在线观看视频| 亚洲蜜桃精久久久久久久久久久久| 欧亚精品一区| 亚洲电影在线| 欧美一级欧美三级| 中文字幕在线播放视频| 色老板在线视频一区二区| 亚洲欧美一区二区三区四区| 久久久久久久久久久久久久久| 日韩成人综合| 国内精品久久久久| 中文字幕日韩国产| 成人丝袜视频网| 神马影院我不卡| 欧美色图天堂| 欧美亚洲禁片免费| 久久久老熟女一区二区三区91| 亚洲最好看的视频| 九色精品美女在线| 在线永久看片免费的视频| 国内精品在线播放| 欧美日韩一区二区视频在线观看| 日本在线人成| 日韩欧美国产骚| 免费黄视频在线观看| 欧美禁忌电影网| 欧美日韩999| 中文字幕乱码中文字幕| 成人免费视频网站在线观看| 欧美一级二级三级| 动漫一区二区| 欧美一区二区视频免费观看| 激情av中文字幕| 欧美在线免费看视频| 久久久久久久999| 影音先锋国产在线| 91论坛在线播放| 9色porny| 日韩成人在线看| 这里只有视频精品| 成人免费毛片视频| 白白色 亚洲乱淫| 国产在线拍揄自揄拍无码| 亚洲日本网址| 亚洲人成在线一二| 日韩一区二区视频在线| 国产99久久久久| 日本精品免费视频| 日韩在线你懂得| 中文字幕精品网| 羞羞色院91蜜桃| 国产日韩欧美精品综合| 无码人妻精品一区二区三区在线| 欧州一区二区三区| 欧美成人合集magnet| 夜夜爽8888| 中文字幕亚洲区| 欧美成年人视频在线观看| 欧洲专线二区三区| 日本中文字幕不卡免费| 好吊视频一区二区三区| 亚洲精品久久久蜜桃| 波多野结衣国产精品| 清纯唯美亚洲综合一区| 国产成人鲁鲁免费视频a| 视频一区二区在线播放| 香蕉影视欧美成人| 少妇一级淫免费观看| 1024日韩| 开心色怡人综合网站| 蜜桃视频在线网站| 亚洲男人av电影| 午夜视频网站在线观看| 国产日韩v精品一区二区| 黄色一级大片在线观看| 怕怕欧美视频免费大全| 国产精品久久久久久久久久小说| 高清av在线| 欧美日本国产视频| 成年人av电影| 成人动漫一区二区| 国产乱子夫妻xx黑人xyx真爽| 亚洲传媒在线| 国产一区二区在线播放| 在线观看欧美日韩电影| 一区二区三区在线视频免费| 亚洲天堂2018av| 亚洲国产老妈| 国产精品久久波多野结衣| 午夜dj在线观看高清视频完整版| 日韩西西人体444www| 欧美卡一卡二卡三| 99视频一区二区三区| 亚洲自偷自拍熟女另类| 亚洲宅男一区| 成人免费福利在线| a国产在线视频| 亚洲小视频在线观看| 国产精品欧美综合| 一区二区三区不卡视频在线观看| 无码人妻精品一区二区三区99不卡| 亚洲看片一区| 亚洲欧洲精品在线| 精品深夜福利视频| 国产日韩欧美中文在线播放| 黄色在线免费| 国产视频久久久久久久| 在线观看国产成人| 亚洲成人你懂的| 亚洲女人毛茸茸高潮| 国产成人亚洲综合色影视| 亚洲国产精品久久久久婷蜜芽| 教室别恋欧美无删减版| 亚洲综合色av| 激情开心成人网| 欧美疯狂xxxx大交乱88av| 三区在线观看| 日韩欧美在线网站| aaa在线视频| 亚洲午夜日本在线观看| 国产美女永久免费无遮挡| 国产精品一级在线| 四虎在线观看| 韩国精品主播一区二区在线观看 | 亚洲国产成人精品女人久久| 国产欧美视频在线观看| 北条麻妃亚洲一区| 日韩中文字幕av电影| 日本a在线天堂| 欧美在线免费看视频| 国内精品久久久久久久果冻传媒| 国产综合色在线观看| 欧美精品电影在线| 成人日韩欧美| 色偷偷av一区二区三区| 外国精品视频在线观看 | 牛牛精品在线视频| 国产亚洲精品美女久久久| 国产手机精品视频| 欧美视频在线一区二区三区 | 婷婷成人激情| 亚洲色图50p| 天天摸天天干天天操| 日韩一区二区三区三四区视频在线观看| 国产成人a v| 在线视频欧美区| 欧美一级淫片免费视频黄| 亚洲成人免费在线| 国产乡下妇女做爰| 亚洲国产aⅴ成人精品无吗| 麻豆天美蜜桃91| 成人免费在线视频观看| 中文字幕成人动漫| 久久精品男人天堂av| 亚洲国产果冻传媒av在线观看| 国产精品亚洲综合一区在线观看| 羞羞的视频在线| 美女性感视频久久| 中文字幕天天干| 免费精品视频在线| 亚洲激情在线看| 国内外成人在线| 欧美69精品久久久久久不卡 | 天天色综合天天| 日本一级黄色大片| 欧美午夜精品久久久久久人妖| 日韩伦人妻无码| 欧美色欧美亚洲高清在线视频| 人人干人人干人人干| 亚洲18色成人| 一级黄色在线视频| 欧美日韩亚洲综合| 国产视频在线一区| 亚洲电影免费观看高清完整版在线观看 | 免费人成精品欧美精品| 农民人伦一区二区三区| 国产一在线精品一区在线观看| 国产精品视频一二三四区| 女人香蕉久久**毛片精品| 亚洲黄色网址在线观看| 1024精品久久久久久久久| 激情图片qvod| 99热免费精品在线观看| 欧美v在线观看| 麻豆一区二区在线| 日本55丰满熟妇厨房伦| 成人h动漫精品| 亚洲av无码一区二区三区人| 久久亚洲私人国产精品va媚药| 魔女鞋交玉足榨精调教| 国产农村妇女精品| 国产一区二区播放| 狠狠色狠狠色综合日日五| 91视频在线视频| 欧美一区二区久久| 凸凹人妻人人澡人人添| 亚洲性av网站| 成年网站在线视频网站| 国产91免费观看| 久久天堂久久| 欧美日韩一区二区三区免费| 精品午夜久久| 国产玉足脚交久久欧美| 天堂午夜影视日韩欧美一区二区| 一道本在线免费视频| 成人免费毛片片v| 国产精品综合激情| 天天影视网天天综合色在线播放| 中文字幕久久久久| 日韩精品视频免费| h网站久久久| 日本欧美在线视频| 91九色鹿精品国产综合久久香蕉| 欧美激情第六页| 欧美日韩国产精品一区二区亚洲| 黄www在线观看| 国产成人亚洲综合a∨婷婷图片 | 91麻豆免费看| 男人的天堂久久久| 欧美亚洲综合一区| 亚洲AV第二区国产精品| 色偷偷噜噜噜亚洲男人的天堂| 久草在线视频资源| 亚洲free嫩bbb| 不卡中文字幕| 99蜜桃臀久久久欧美精品网站| 国产乱子伦一区二区三区国色天香| 欧美 日本 国产| 亚洲图片有声小说| 精品黑人一区二区三区在线观看| 亚洲美女av在线播放| 日本天码aⅴ片在线电影网站| 国产精品免费在线免费| 久久精品国产亚洲5555| 亚洲 欧洲 日韩| 蜜臀av国产精品久久久久| 国产美女视频免费观看下载软件| 亚洲免费毛片网站| 亚洲视频在线观看一区二区 | 91久久久久久| 日本激情一区| wwwwxxxx日韩| 国产亚洲一区二区在线观看| 青娱乐国产在线视频| 欧美日韩一本到| 日本中文字幕在线视频| 国产精品久久久久不卡| 日韩欧美黄色| 18禁免费观看网站| 99在线精品一区二区三区| 久久精品这里只有精品| 7777精品伊人久久久大香线蕉经典版下载| 国产一级二级三级在线观看| 久久久久久久久久久成人| 成人免费观看49www在线观看| 亚洲a∨一区二区三区| 久久久xxx| 欧美大波大乳巨大乳| 一本大道综合伊人精品热热| 成人乱码一区二区三区| 欧美黄色免费网站| 国产精品xxx在线观看| 久久精品xxx| 99久久国产综合精品色伊 | 99精品国产在热久久| 性xxxxxxxxx| 亚洲成人一区二区在线观看| 99在线精品视频免费观看20| 中文字幕在线看视频国产欧美| 免费在线观看一区| 一区二区高清视频| 国产精品综合视频| 国产主播在线播放| 日韩风俗一区 二区| 久久人体大尺度| 亚洲国产精品123| 国产一区视频网站| 免费一级特黄特色大片| 亚洲国产欧美一区二区丝袜黑人| 成av人片在线观看www| 久久精品中文字幕一区二区三区| 性欧美暴力猛交另类hd| 级毛片内射视频| 欧美一级在线观看| 爱搞国产精品| 亚洲高清不卡一区| 国产一区二区精品久久| 久久精品亚洲无码| 亚洲日本中文字幕| 国产精品亚洲欧美日韩一区在线| 国产精品视频一二三四区| 91在线播放网址| 一级黄在线观看| 国内精品一区二区三区四区| 久久影院资源站| 天天爽人人爽夜夜爽| 亚洲精品免费在线| 深夜福利在线视频| 国产深夜精品福利| 99精品福利视频| 亚洲伦理一区二区三区| 亚洲精品一区二区三区影院| 精品极品在线| 亚洲啪啪av| 99riav久久精品riav| 在线视频 中文字幕| 久久久在线观看| 欧美大人香蕉在线| 黄色a一级视频| 日韩欧美自拍偷拍| 国内精品伊人|