精品欧美一区二区三区在线观看 _久久久久国色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| 私拍精品福利视频在线一区| 欧美亚洲综合色| 亚洲激情免费视频| 桃花色综合影院| 免费在线观看视频一区| 欧美黑人性视频| 伊人网在线视频观看| av国产精品| 日韩欧美aⅴ综合网站发布| 亚洲看片网站| 婷婷五月综合久久中文字幕| 麻豆精品国产91久久久久久 | 爱情岛亚洲播放路线| 国产日产亚洲精品系列| 国产精品一区二区三区四区五区 | 男女激烈动态图| 日本天堂在线| 国产91露脸合集magnet| 国产精品一区二区三区免费视频| 国产精品6666| 亚洲精品成人| 色综合影院在线| 精品少妇人妻一区二区黑料社区| 久久av偷拍| 欧美日韩视频第一区| 欧美极品欧美精品欧美| 尤物yw193can在线观看| 国产精品激情偷乱一区二区∴| 韩国一区二区三区美女美女秀| 国产口爆吞精一区二区| 免费观看久久久4p| 国产成人精品优优av| 国产一区二区三区影院| 欧美久久99| 久久精品视频va| www.99热| 欧美裸体在线版观看完整版| 精品亚洲国产成av人片传媒| 小毛片在线观看| 凹凸av导航大全精品| 日韩一级大片在线观看| 红桃视频 国产| 成人国产综合| 欧美视频日韩视频在线观看| 成人一级片网站| 在线天堂资源| 色综合久久天天| 精品国产一二三四区| 日本在线影院| 欧美日韩一区免费| 丰满少妇被猛烈进入高清播放| av免费不卡国产观看| 亚洲国产综合视频在线观看| 国产精品国三级国产av| 国产精品186在线观看在线播放| 亚洲黄一区二区三区| 97在线免费视频观看| 污污网站在线看| 亚洲一区二区欧美激情| 亚洲 自拍 另类小说综合图区| 爱情岛亚洲播放路线| 五月开心婷婷久久| 国产主播在线看| 欧美黑人疯狂性受xxxxx野外| 色94色欧美sute亚洲线路二 | 亚洲天堂网在线观看| 男人天堂av电影| 日韩国产一区二区三区| 久久久精品国产一区二区| av激情在线观看| 伊人久久成人| 国产91在线播放| 做爰无遮挡三级| 国产在线精品一区二区夜色| 亚洲va欧美va国产综合剧情| 精品久久久中文字幕人妻| 成人综合在线观看| 欧美乱偷一区二区三区在线| 人人干在线视频| 亚洲综合视频在线观看| 91视频 -- 69xx| www.精品国产| 欧美成人精品二区三区99精品| 日本黄色片在线播放| 不卡在线一区| 欧美精品成人在线| 国产成人精品777777| 精品亚洲成a人在线观看| 国产精品乱码视频| www.国产精品.com| 夜夜夜精品看看| 北条麻妃av高潮尖叫在线观看| 91丨精品丨国产| 精品99999| 久久久久亚洲AV成人无在| 欧美99久久| 国产91在线播放九色快色| 国产高清精品软件丝瓜软件| 26uuu亚洲综合色欧美| 曰韩不卡视频| www.成人影院| 日韩精品一区二区三区老鸭窝| 久久久久久久久久久久久久久| 中文乱码免费一区二区三区下载| 人体精品一二三区| 亚洲国产精品一| 中文字幕乱码日本亚洲一区二区| 妺妺窝人体色777777| 国产精品成人国产| 日韩精品视频免费专区在线播放| 91免费公开视频| 日韩av一区二区在线影视| 国产精品久久久久免费| 国产欧美久久久久久久久| 色婷婷亚洲一区二区三区| 无码人妻久久一区二区三区蜜桃| 国产99久久精品一区二区300| 欧美丰满少妇xxxxx| 亚洲图片小说视频| 久久精品一二三| 婷婷无套内射影院| 97超碰成人| 久久影视免费观看| 一区二区三区精| 国产农村妇女毛片精品久久麻豆| 女人天堂av手机在线| 91大神精品| 欧美成人在线免费视频| 国产精品高潮呻吟AV无码| 国产女人aaa级久久久级| 国产成人久久777777| 亚洲97av| 欧美自拍视频在线观看| 天天干天天干天天干| 亚洲国产乱码最新视频| 免费不卡av网站| 999国产精品| 国产欧美最新羞羞视频在线观看| 成人在线观看免费| 91久久国产最好的精华液| 亚洲永久无码7777kkk| 一本色道久久综合亚洲精品不卡| 国产91视觉| 欧美精品videossex少妇| 日韩一区二区三| 欧美精品乱码视频一二专区| 国产一区二区久久| 欧美视频在线第一页| 日韩中文字幕无砖| 久久久久久成人| 天堂网在线资源| 黑人巨大精品欧美一区二区三区| 亚洲乱码国产乱码精品精大量| 国产欧美日韩亚洲一区二区三区| 久久精品一二三区| 午夜欧美巨大性欧美巨大| 国产亚洲美女久久| 在线不卡免费视频| 亚洲视频中文字幕| www.四虎精品| 国产精品亚洲综合色区韩国| 欧美在线一区二区三区四区| 97久久网站| 欧美wwwxxxx| 日韩一级片免费看| 日本韩国精品在线| 激情五月激情综合| 国产不卡在线播放| 丝袜老师办公室里做好紧好爽| 精品中文一区| 成人黄色片网站| 欧美aaa免费| 国产视频精品xxxx| 国产又粗又猛视频免费| 一区二区三区精品视频| 国产伦精品一区三区精东| 久久精品女人天堂| 国产精品12p| 里番精品3d一二三区| 国产成人精品网站| 2024最新电影免费在线观看| 亚洲精品电影在线观看| 中文区中文字幕免费看| 亚洲狠狠丁香婷婷综合久久久| 欧美一区二区免费在线观看| 日韩高清在线观看| 欧美黄网在线观看| 欧美精品色图| 成人h在线播放| 高清成人在线| 欧美极品少妇全裸体| 成人免费黄色网页| 亚洲第一av网站| 黄色av网站免费观看| 一区二区三区在线视频播放| 一级片手机在线观看| 国产精品资源网| 日韩毛片在线免费看| 午夜国产精品视频免费体验区| 蜜桃传媒视频麻豆一区| 精品亚洲a∨一区二区三区18| 欧美又大又粗又长| 午夜伦理大片视频在线观看| 一色桃子一区二区| 午夜国产在线观看| 欧美mv日韩mv国产| 一级特黄aaa大片在线观看| 大荫蒂欧美视频另类xxxx| 懂色av懂色av粉嫩av| 中文字幕精品—区二区四季| 三级视频网站在线观看| 国产麻豆午夜三级精品| 美女喷白浆视频| 亚洲综合不卡| 中文字幕无码精品亚洲资源网久久| 日韩精品诱惑一区?区三区| 精品国产免费一区二区三区| 国产专区精品| 国产精品自产拍高潮在线观看| gay欧美网站| 欧美亚州一区二区三区| 麻豆av在线免费观看| 麻豆成人在线看| 在线观看av黄网站永久| 亚洲天堂av在线免费观看| 特黄视频在线观看| 亚洲第一精品自拍| 亚洲不卡免费视频| 91精品国产欧美日韩| 91精东传媒理伦片在线观看| 欧美性受xxxx黑人xyx| 欧美超碰在线观看| 色悠悠亚洲一区二区| 欧美日韩乱国产| 精品美女永久免费视频| 国产精品第二十页| 午夜影视日本亚洲欧洲精品| 国产亚洲第一页| 亚洲国产精品人人做人人爽| 欧美交换国产一区内射| 亚洲综合色婷婷| 国产亚洲欧美精品久久久久久| 亚洲精品国产无套在线观| 欧美激情图片小说| 洋洋成人永久网站入口| 国产真实的和子乱拍在线观看| 亚洲一区视频在线| 国产午夜福利精品| 五月综合激情日本mⅴ| 国偷自拍第113页| 欧美日韩综合视频网址| 亚洲国产av一区二区三区| 色婷婷综合中文久久一本| 无码aⅴ精品一区二区三区| 欧美三区免费完整视频在线观看| 中文字幕 人妻熟女| 欧美日韩激情在线| 国产三级伦理片| 91精品国产福利| 免费看av毛片| 亚洲人成电影在线观看天堂色| jizzjizz在线观看| 日韩中文字幕亚洲| 日本在线视频中文有码| 亚洲2020天天堂在线观看| 欧美性xxx| 国产日韩专区在线| swag国产精品一区二区| 欧美不卡三区| 日韩精品一卡| 青青青青在线视频| 视频一区二区中文字幕| 毛片毛片毛片毛| 成人小视频免费在线观看| 久久久久久国产精品无码| 国产精品成人网| 久久人人爽人人爽人人| 日韩欧美在线网址| 国产一区二区三区在线观看| 欧美成人欧美edvon| 你懂的好爽在线观看| 日韩视频免费大全中文字幕| 国产精品蜜臀| 国产精品视频最多的网站| 日韩一区二区三区在线看| 久久人人九九| 亚洲国产日韩欧美在线| 无码精品国产一区二区三区免费| 久久成人久久爱| 手机在线看片日韩| 综合在线观看色| 中文字幕第四页| 欧美一区二区三区在线电影| 日韩有码电影| 欧美久久精品午夜青青大伊人| 91久久国产综合久久91猫猫| 91免费视频网站| 教室别恋欧美无删减版| 精品人妻大屁股白浆无码| 日本不卡的三区四区五区| 国产精品无码自拍| 国产精品久久久久天堂| 在线观看亚洲欧美| 日韩欧美国产成人一区二区| 黄色在线免费观看大全| 久久久久久久999| 91麻豆精品国产综合久久久| 日韩一区免费观看| 在线亚洲自拍| 色哟哟网站在线观看| 国产精品污网站| 国语对白永久免费| 精品999久久久| 性直播体位视频在线观看| 国产中文日韩欧美| av伊人久久| 黑鬼大战白妞高潮喷白浆| aaa亚洲精品| 黄色一级片在线| 欧美一区二区三区在线看| 日本视频在线| 国产欧美一区二区三区在线看| 国产亚洲欧美日韩在线观看一区二区 | 欧美在线免费看| 加勒比久久高清| 97超碰在线视| 国产高清精品久久久久| 黄色香蕉视频在线观看| 777亚洲妇女| 拍真实国产伦偷精品| 国产精品视频久| 日本欧美视频| 天天干在线影院| 中文字幕乱码一区二区免费| 男操女视频网站| 亚洲一区av在线播放| 亚洲播播91| 日韩电影大全在线观看| 天堂va蜜桃一区二区三区漫画版| 亚洲狠狠婷婷综合久久久久图片| 亚洲成人动漫av| 偷拍精品一区二区三区| 国模精品系列视频| 欧美三级午夜理伦三级在线观看| 国产妇女馒头高清泬20p多| 国产成人av网站| 久久综合激情网| 亚洲精品美女网站| 欧美aa视频| 午夜精品一区二区三区在线观看| 日韩av网站免费在线| 超碰人人干人人| 69久久99精品久久久久婷婷| fc2ppv国产精品久久| av一区观看| 国产日韩欧美一区| 国产精品一二三区在线观看| 欧美中文字幕一区二区三区| 中文字幕在线观看日本| 成人免费淫片视频软件| 欧美久久成人| www.自拍偷拍| 欧美日韩一区二区三区四区 | 欧美日韩国产精品一区二区三区四区| 日韩在线视频免费| 日本成人免费在线| 久久一区二区三区喷水| 国产chinesehd精品露脸| 精品久久久久久国产| 国产毛片在线看| 91久久久精品| 国产日本精品| 懂色av蜜臀av粉嫩av永久| 精品免费视频一区二区| 中文字幕在线视频久| 亚洲啪啪av| 国产福利91精品| 男人天堂2024| 美女精品视频一区| 色婷婷久久久| 一二三av在线| 欧美日韩人人澡狠狠躁视频| 色影视在线观看| 国产伦精品一区二区三区照片91| 久久中文字幕一区二区三区| 搜索黄色一级片| 国产视频亚洲视频| 日韩毛片免费视频一级特黄| 欧美精品久久久久久久自慰| 国产精品色婷婷久久58| 丰满熟妇乱又伦| 国产精品香蕉在线观看| 亚洲精品一级| 日韩欧美123区| 亚洲欧美中文另类| 2020最新国产精品| 一级片视频免费观看|