JVM Class详解之一
首先看Class中包含哪些信息簡單的說所有java文件中有的信息class文件都有,編譯器幫我們將java文件轉化成了JVM能看懂的class格式而已
Class 概述
Class文件是一組以8位字節為基礎的二進制流,各個數據項目按照嚴格順序緊湊排列在Class文件中。
所有的16位,32位,64位長度的數據將被構造成2個,4個,8個字節單位來標示。
ClassFile結構
| u4 | magic | 1 |
| u2 | minor_version | 1 |
| u2 | major_version | 1 |
| u2 | constant_pool_count | 1 |
| cp_info | constant_pool | constant_pool_count-1 |
| u2 | access_flags | 1 |
| u2 | this_class | 1 |
| u2 | super_class | 1 |
| u2 | interfaces_count | 1 |
| u2 | interfaces | interfaces_count |
| u2 | fields_count | 1 |
| field_info | fields | fields_count |
| u2 | methods_count | 1 |
| method_info | methods | methods_count |
| u2 | attributes_count | 1 |
| attribute_info | attributes | attributes_count |
class格式說明
- magic:魔數,魔數的唯一作用是確定這個文件是否為一個能被虛擬機所接受的Class文件。魔數值固定為0xCAFEBABE
- minor_version、major_version: 分別為Class文件的副版本和主版本
- constant_pool_count: 常量池計數器,constant_pool_count的值等于constant_pool表中的成員數加1
- constant_pool[]: 常量池,constant_pool是一種表結構,它包含Class文件結構及其子結構中引用的所有字符串常量、類或接口名、字段名和其它常量。常量池不同于其他,索引從1開始到constant_pool_count?-1
- access_flags: 訪問標志,access_flags是一種掩碼標志,用于表示某個類或者接口的訪問權限及基礎屬性
- this_class: 類索引,this_class的值必須是對constant_pool表中項目的一個有效索引值
- super_class: 父類索引
- interfaces_count: 接口計數器,interfaces_count的值表示當前類或接口的直接父接口數量
- interfaces[]: 接口表,interfaces[]數組中的每個成員的值必須是一個對constant_pool表中項目的一個有效索引值,它的長度為interfaces_count
- fields_count: 字段計數器
- fields[]: 字段表,fields[]數組中的每個成員都必須是一個fields_info結構的數據項
- methods_count: 方法計數器
- methods[]: 方法表,methods[]數組中的每個成員都必須是一個method_info結構的數據項
- attributes_count: 屬性計數器
- attributes[]: 屬性表,attributes表的每個項的值必須是attribute_info結構
廢話不多說HelloWorld搞起
public class HelloWorld { String str = "";public String getStr() { return str; }public void setStr(String str) { this.str = str; } } 編譯成class文件以后,只用javap -verbose HelloWorld.class 指令可以查看當前class的內容
同時使用UE打開class文件
我們來一起看下ClassFile結構
前4個字節為魔數,也就是0xCAFEBABE,這里都是十六進制
魔數后2個字節為副版本號,這里副版本號是0
主版本號0x0033,轉為十進制,主版本號是51 標示當前class是通過jdk 1.7編譯的,0x32是jdk1.6 0x31是jdk1.5
這兩個字節是常量池計數器,常量池的數量為0x001A,轉為十進制是26,也就是說常量池索引為1~26
從常量池開始
常量池計數器后面緊跟著就是常量池的內容
所有的常量池項都具有如下通用格式:
后面的0x07對應tag找到是CONSTANT_Class,標示接下來的是一個class的信息。后面的 00 02 是class的name_index 標示指向常量池的第二個常量。我們再看第二個常量
第二個常量是01開頭,我們查看常量類型表中對應是Utf-8,再按照utf-8的結構,后面的00 0A代表了這個utf-8的長度這里長度轉換為10進制是11,后面緊跟著utf-8的實際內容
再后面0x 07,是常量池的下一個產量,也是一個class信息,后面跟00 04,name_index執行常量池的
第4個常量。
第4個常量又是utf-8,后面長度為 0x10 十進制為16,接下來的為實際內容
接下來都可以按照此方法分析。
直觀結果可以通過javap指令查看
常量池后面緊跟的2個字節是Access Flag,這個表示用于標示類或接口層次的訪問信息,如這個Class是類還是接口,是否為public類型,是否定義為abstrace類型。
標志名稱 標志值 含義
ACC_PUBLIC 0x0001 是否為public類型
ACC_FINAL 0x0010 是否被聲明為final,只有類可設置
ACC_SUPER 0x0020 是否允許使用invokespecial字節碼指令,JDK1.2以后編譯出來的類這個標志為真
ACC_INTERFACE 0x0200 標識這是一個接口
ACC_ABSTRACT 0x0400 是否為abstract類型,對于接口和抽象類,此標志為真,其它類為假
ACC_SYNTHETIC 0x1000 標識別這個類并非由用戶代碼產生
ACC_ANNOTATION 0x2000 標識這是一個注解
ACC_ENUM 0x4000 標識這是一個枚舉
我們這里0021標示為public Class
接下來的是類索引,父類索引與接口索引集合
this_class,super_class,interfaces_count,interfaces
類索引
為2個字節
這里為00 01,指向常量池中第一個常量,之前我們分析過常量池中第一個常量為Class類型,內容指向第二個常量UTF-8的HelloWorld。
標示當前名為HelloWorld
父類索引
也是2個字節
指向常量中第三個常量,對應內容為java/lang/Object
接口數量
表示接口數量為0
字段表集合
00 01 標示字段數量為1
字段表的格式如下
| u2 | access_flags | 1 |
| u2 | name_index | 1 |
| u2 | descriptor_index | 1 |
| u2 | attributes_count | 1 |
| attribute_info | attributes | attributes_coun |
accessFlags為 00 00 當時當前字段無修飾符
字段修飾符格式如下
| ACC_PUBLIC | 0x0001 | 字段是否為public |
| ACC_PRIVATE | 0x0002 | 字段是否為private |
| ACC_PROTECTED | 0x0004 | 字段是否為protected |
| ACC_STATIC | 0x0008 | 字段是否為static |
| ACC_FINAL | 0x0010 | 字段是否為final |
| ACC_VOLATILE | 0x0040 | 字段是否為volatile |
| ACC_TRANSIENT | 0x0080 | 字段是否為transient |
| ACC_SYNTHETIC | 0x1000 | 字段是否為編譯器自動產生 |
| ACC_ENUM | 0x4000 | 字段是否為enum |
name_index為 00 05指向常量池中的第五個常量
第5個常量為str,變量名為str
descriptor_index指向常量池第6個變量,為Ljava/lang/String類型
attributes_count(屬性計數器,占2字節,0x0000,所以該字段沒有額外需要描述的信息)
方法集合
method_count: 00 03 有3個方法
methods:方法表集合
| u2 | access_flags | 1 |
| u2 | name_index | 1 |
| u2 | descriptor_index | 1 |
| u2 | attributes_count | 1 |
| attribute_info | attributes | attributes_coun |
access flags的定義見下表
| ACC_PUBLIC | 0x0001 | 字段是否為public |
| ACC_PRIVATE | 0x0002 | 字段是否為private |
| ACC_PROTECTED | 0x0004 | 字段是否為protected |
| ACC_STATIC | 0x0008 | 字段是否為static |
| ACC_FINAL | 0x0010 | 字段是否為final |
| ACC_SYNCHRONIZED | 0x0020 | 字段是否為synchronized |
| ACC_BRIDGE | 0x0040 | 方法是否是由編譯器產生的橋接方法 |
| ACC_VARARGS | 0x0080 | 方法是否接受不定參數 |
| ACC_NATIVE | 0x0100 | 字段是否為native |
| ACC_ABSTRACT | 0x0400 | 字段是否為abstract |
| ACC_STRICTFP | 0x0800 | 字段是否為strictfp |
| ACC_SYNTHETIC | 0x1000 | 字段是否為編譯器自動產生 |
這里方法access flags 為 00 01 說明方法為public的
name_index為00 07,方法名指向常量中第7個常量方法名為, descriptor_index為常量池第8個常量()V
attributes_count 為 00 01標示這個方法的屬性表集合中有一個屬性。屬性名稱為接下來2位0x0009,指向常量池中第9個常量:Code
接下來4位為 00 00 00 3D標示Code屬性值的字節長度為3D,接下來為00 02標示該方法的操作數棧的深度最大值為2.
00 01標示該方法的局部變量占用空間為1.
接下來4位00 00 00 0B 為機器編譯生成字節碼指令的長度為11,后面11個字節就是字節碼指令(字節碼指令可查詢虛擬機字節碼指令表),這里字節碼指令長度用4個字節標示,所有字節碼指令超長Class編譯會失敗的。
再接下來為 00 00標示Code屬性異常表結合為空。
再后面為 00 02,,說明Code帶有2個屬性, 00 10即為Code屬性第一個屬性的屬性名成指向常量池中第16個常量
接下來的00 00 00 0E 標示LinueNumberTable屬性值所占字節長度為15.接下來2位 00 03標示該line number table中有3個line number table表,start pc為 00 00 line number第 00 01個為00 04 第 00 02個為 00 0A?
再后面的 00 01又是第二個方法的access flags,接著開始第二個方法。
from:?https://yq.aliyun.com/articles/7241?spm=5176.100239.blogcont37179.10.F6pHuW
總結
以上是生活随笔為你收集整理的JVM Class详解之一的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Jvm原理剖析与调优之内存结构
- 下一篇: JVM Class字节码之三-使用BCE