java class 檔案格式解析

tomj2ee發表於2022-04-16

前言

大約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個位元組

  1. magic 佔用4個位元組,(ca fe ba be )

image

  1. minor_version 子版本號 ,2個位元組數字
    image

  2. major_version 主版本好 2個位元組的數字

image

  1. constant_pool_count 常量池數目 2個位元組的數字

image

  1. constant_pool[constant_pool_count-1] 常量池陣列

image

  1. access_flags 訪問標識 2個位元組數字
  2. this_class class名稱的索引,
  3. super_class 超類的名稱索引
  4. interfaces_count 介面的數目
  5. interfaces[interfaces_count] 介面的陣列
  6. fields_count 欄位數目
  7. fields[fields_count] 欄位的陣列
  8. methods_count 方法的數目
  9. methods[methods_count] 方法的陣列
  10. attributes_count 屬性的數目
  11. attributes[attributes_count] 屬性的陣列

如何自己動手解一個class檔案

相信大部分第一樣看到上面的協議時候,能看弄,但是要自己動手解析出每個欄位的含義出來,
就無法下手了,

  1. 讀取class檔案
 FileInputStream in= new FileInputStream("d:/my.class");
  1. 讀取 magic ,(magic u4 佔用4個位元組)
 byte[] bytes=new byte[4];
       in.read(bytes);
  1. 讀取 minor_version u2 佔用2個位元組
 byte[] minorByte=new byte[2];
       in.read(minorByte);
  1. 讀取 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 哦

相關文章