前言
大約5年前,想研究javaassistant,cglib等位元組碼操作的相關類庫,來對class進行增強,當要到要操作位元組碼的時候,發現無法繼續下去了,只能放棄。
學習jvm字碼,需要理解class的組成方式,對彙編,操作棧比較瞭解,無奈,只好重新學習編譯原理,彙編等知識,再來看jvm規範,現在理解起來,容易很多了。
Class檔案規範
編譯後被 Java 虛擬機器所執行的程式碼使用了一種平臺中立(不依賴於特定硬體及作業系統的)
的二進位制格式來表示,並且經常(但並非絕對)以檔案的形式儲存,因此這種格式被稱為 Class
檔案格式。Class 檔案格式中精確地定義了類與介面的表示形式,包括在平臺相關的目標檔案格
式中一些細節上的慣例
相關文件
https://docs.oracle.com/javase/specs/jvms/se15/html/jvms-4.html
ClassFile {
u4 magic;
u2 minor_version;
u2 major_version;
u2 constant_pool_count;
cp_info constant_pool[constant_pool_count-1];
u2 access_flags;
u2 this_class;
u2 super_class;
u2 interfaces_count;
u2 interfaces[interfaces_count];
u2 fields_count;
field_info fields[fields_count];
u2 methods_count;
method_info methods[methods_count];
u2 attributes_count;
attribute_info attributes[attributes_count];
}
下面,我們開始解析每個欄位是如何標識出來的
其中 u4, u2 代表什麼意思
u 表示無符號數 後面的數字 表示 佔用多少位元組
u4 佔用4個位元組
u2 佔用2個位元組
- magic 佔用4個位元組,(ca fe ba be )
-
minor_version 子版本號 ,2個位元組數字
-
major_version 主版本好 2個位元組的數字
- constant_pool_count 常量池數目 2個位元組的數字
- constant_pool[constant_pool_count-1] 常量池陣列
- access_flags 訪問標識 2個位元組數字
- this_class class名稱的索引,
- super_class 超類的名稱索引
- interfaces_count 介面的數目
- interfaces[interfaces_count] 介面的陣列
- fields_count 欄位數目
- fields[fields_count] 欄位的陣列
- methods_count 方法的數目
- methods[methods_count] 方法的陣列
- attributes_count 屬性的數目
- attributes[attributes_count] 屬性的陣列
如何自己動手解一個class檔案
相信大部分第一樣看到上面的協議時候,能看弄,但是要自己動手解析出每個欄位的含義出來,
就無法下手了,
- 讀取class檔案
FileInputStream in= new FileInputStream("d:/my.class");
- 讀取 magic ,(magic u4 佔用4個位元組)
byte[] bytes=new byte[4];
in.read(bytes);
- 讀取 minor_version u2 佔用2個位元組
byte[] minorByte=new byte[2];
in.read(minorByte);
- 讀取 major_version u2 佔用2個位元組
byte[] majorVersion=new byte[2];
in.read(majorVersion);
看到上面的解析,是否明白了,其實還是很有規律的,只要你認真看協議文件(要看好多遍才行)
最終解析class 文件就是這樣的
ClassFile classFile = new ClassFile();
PcBufferInputStream in = new PcBufferInputStream(new FileInputStream(fileName));
classFile.setMagic(readMagic(in));
classFile.setMinorVersion(readMinorVersion(in));
classFile.setMajorVersion(readMajorVersion(in));
classFile.setConstantPoolCount(readConstantPoolCount(in));
classFile.setCpInfo(readCpInfo(in));
classFile.setAccessFlags(readAccessFlags(in));
classFile.setThisClass(readThisClass(in));
classFile.setSuperClass(readSuperClass(in));
classFile.setInterfacesCount(readInterfacesCount(in));
// u2 interfaces interfaces_count
classFile.setInterfaces(readInterfaces(in));
// u2 fields_count
classFile.setFieldsCount(readFieldsCount(in));
// field_info fields fields_count
classFile.setFields(readFields(in));
// u2 methods_count 1
// method_info methods methods_count
classFile.setMethodsCount(readMethodsCount(in));
classFile.setMethods(readMethods(in));
// u2 attribute_count 1
classFile.setAttributeCount(readAttributeCount(in));
// attribute_info attributes attributes_count
classFile.setAttributes(readAttributes(in));
classFile.setPcRecord(recordMap);
return classFile;
java class 解析原始碼開源地址
https://gitee.com/venus-suite/java-classViewer
如果喜歡,歡迎stars 哦