JVM学习笔记(六)Java Class类文件结构

Java语言从诞生之时就宣称一次编写,到处运行的跨平台特性,其实现原理是源码文件并没有直接编译成机器指令,而是编译成Java虚拟机可以识别和运行的字节码文件(Class类文件,*.class),字节码文件是一种平台无关的中间编译结果,字节码文件由java虚拟机读取,解析和执行,java虚拟机屏蔽了不同操作系统和硬件平台的差异性。

  如今的java虚拟机已经称为一种通用平台,不但能够运行java语言,Groovy,JRuby,Jython等一大批动态语言也可以直接在Java虚拟机上运行,其原理也是这些动态语言的编译器将源码文件编译为和Java相同的字节码文件,这样Java虚拟机就可以像执行java语言一样执行这些动态语言了。

  字节码class类文件是由一系列字节码命令组成,用于表示程序中各种常量、变量、关键字和运算符号的语义等等。Java的Class类文件是一组以8为字节为单位的二进制流,各个数据项严格按照顺序紧凑地排列在Class类文件之中,中间没有添加任何分隔符,当遇到需要占用8位字节以上空间的数据项时,按照高位在前的方式分割成若干个8位字节进行存储。

  Java虚拟机规定,Class类文件格式采用类似C语言结构体的伪结构来存储,这种伪结构中只有两种数据类型:无符号数和表:

(1).无符号数:
属于基本类型的数据,以u1, u2, u4, u8来分别代表1个字节,2个字节,4个字节和8个字节的无符号数,无符号数可以用来描述数字、索引引用、数量值或者按照UTF-8编码的字符串值。
(2).表:
由多个无符号数或其他表作为数据项构成的复合数据类型,所以表都习惯性地以“_info“结尾。表用于描述有层次关系的复合结构数据,整个Class文件本质就是一张表。

Java Class类文件结构如下:

类型名称数量
u4magic1
u2minor_version1
u2major_version1
u2constant_pool_count1
cp_infoconstant_poolconstant_pool_count-1
u2access_flags1
u2this_class1
u2super_class1
u2interfaces_count1
u2interfacesinterfaces_count
u2fields_count1
field_infofieldsfields_count
u2methods_count1
method_infomethodsmethods_count
u2attributes_count1
attribute_infoattributesattributes_count

无论是符号数还是表,当需要描述同一类型但数量不定的多个数据时,经常会使用一个前置的容量计数器加若干个连续的数据项的形式,这时称这一系列连续的某一类型的数据为某一类型的集合。

Class类文件没有任何分隔符,是严格按照这个结构表顺序排列,下面具体介绍各个名称含义:

1、魔数-magic:

每个Class文件的头4个字节被称为魔数(Magic Number),它的唯一作用是用于确定这个文件是否为一个能被java虚拟机所接收的Class类文件,即用于判定文件是否是符合规范的java Class文件。虽然说后缀名“.class”可以表明文件是一个Class文件,但是文件后缀名是可以随意被改动的,基于安全的考虑,很多文件都通过魔数值来唯一确定文件类型,java的Class文件魔数是:0xCAFEBABE.

2、版本号-minor_version和major_version

每个Class文件的第5和第6个字节代表Class文件的次版本号(Minor Version),第7和第8个字节代表Class文件的主版本号(Major Version)。
Class文件的主、次版本号是由JDK决定的,JDK1.0~JDK1.1使用了45.0~45.3的版本号(45是主版本好,点”.“之后的是次版本号),从JDK1.1开始,每个大版本的JDK主版本号加1.
Class主、次版本号的一个作用时,高版本的Java虚拟机可以向前兼容,运行低版本JDK编译的Class字节码文件,而低版本的java虚拟机不能运行高版本JDK编译的Class字节码文件。当低版本的java虚拟机运行高版本JDK编译的Class字节码文件时,通常会报类似如下的异常:

Exception in thread "main" java.lang.UnsupportedClassVersionError: a (Unsupporte  
d major.minor version 49.0)  

JDK1.0~JDK1.1使用了45.0~45.3的版本号,JDK1.2使用了46.0~46.65535的版本号,JDK1.3使用了47.0~47.65535的版本号,JDK1.4使用了48.0~48.65535的版本号,JDK1.5使用了49.0~49.65535的版本号,JDK1.6使用了50.0~50.65535的版本号,JDK1.7使用51.0~51.65535的版本号。
在编译时可以通过指定-target参数来改变主版本号,如JDK1.6编译时如果没有给定target参数,则编译出来的Class文件的主版本号是50,如果给定”-target 1.4 -source 1.4”参数之后,则主版本将变为48,如果给定”-target 1.5 ”参数之后,则主版本将变为49。

3、 常量池-constant_pool_count和constant_pool:

constant_pool_count代表Class文件中常量池的数目,由于常量池的计数从1开始,因此常量池的容量是constant_pool_count-1。
第0项常量空出做特殊考虑,为了满足一些指向常量池的索引值在某些特定情况下需要表达“不指向任何一个常量池”的意思。
constant_pool常量池是Class类文件中出现的第一个表类型数据,常量池主要存放两大类常量:
a.字面量(Literal):包括文本字符串、final类型常量值。
b.符号引用(SymbolicReferences):包括类和接口的全限定名、字段的名称和描述符、方 法的名称和描述符。

4、访问标志 - access_flags:

访问标志占两个字节,用于表示Class或接口层次的访问标志,即类或接口层面的访问控制信息,通常存储的信息包括:Class类文件是类、接口、枚举或是注解;是否定义为public类型;是否定义为abstract类型;类是否被定义为final等等。

5、this_class、super_class和interfaces:

this_class类索引用于确定类的全限定名,super_class父类索引用于确定父类的全限定名,interfaces接口索引用于确定接口的全限定名,由于java中可以实现多个接口,因此使用interfaces_count来存储接口数量。

6、字段表-field_info:

field_info字段表用于描述接口或者类中声明的变量,field字段包括了类级变量(静态变量)和实例级变量(成员变量),但不包括方法内部的局部变量。
fields_count字段数目表示Class文件中的类和实例变量总数,字段存放的信息包括:字段访问标志、是否静态、是否final、是否并发可见volatile、是否可序列化transient、数据类型、字段名称等等。
注意:字段表中不包含从父类或者接口中继承而来的字段,但是会添加原本代码中不存在的字段,例如this,以及内部类对外部类访问而自动添加的外部类实例字段等。

7、方法表-method_info:

method_info方法表用于描述类或者接口中声明的方法,methods_count用于表示Class文件中方法总数,method方法存储了方法的访问标识、是否静态、是否final、是否同步synchronized、是否本地方法native、是否抽象方法abstract、方法返回值类型、方法名称、方法参数列表等信息。
方法的代码指令并没有直接存放在方法表中,而是存放着属性表中的方法表Code中。
注意:如果父类的方法在子类没有被重写,方法表中不会出现来自父类的方法信息,但是编译器会自动添加类构造器””方法和实例构造器””方法。
Java编译器的方法特征签名只包括:方法名称、参数顺序和参数类型,不包括方法返回值类型,因此java的方法重载不能通过方法的返回值类区别,但是在Class文件中,方法特征签名包括方法的返回值类型,因此Class文件中可以共存两个名称和参数完全相同而返回值类型不同的方法。

8、属性表-attribute_info:
attribute_info属性表是Class文件格式中最具扩展性的一种数据项目,用于存放field_info字段表、method_info方法表以及Class文件的专有信息,属性表不要求各个属性有严格顺序,只要求不与已有的属性名字重复即可,属性表中存放的常用信息如下:

属性名称使用位置含义
Code方法表Java代码编译后的字节码指令
ConstantValue
字段表final关键字定义的常量值
Deprecated类、方法表、字段表被声明为Deprecated的字段或方法
Exception方法表方法抛出的异常
InnerClasses类文件内部类列表
LineNumberTableCode属性java源码行号和字节码指令的对应关系
LocalVariableTableCode属性方法的局部变量描述
SourceFile类文件源文件名称
Synthetic类、方法表、字段表标识方法或字段为编译器自动生成

Class文件是二进制文件,使用支持二进制的文本编辑器打开之后显示的全是二进制数据,非常的不便于阅读和理解,使用JDK提供的javap工具可以简单将Class反编译,编译理解Class文件的结构,例子如下:

源码:

public class Test {    
    public int getNum(int i) {    
        return i + 1;    
    }    
}    

javap反编译之后的字节码文件:

public class Test extends java.lang.Object    
  SourceFile: "Test.java"    
  minor version: 0    
  major version: 50    
//常量池  
 Constant pool:    
const #1 = class        #2;       
const #2 = Asciz        Test;    
const #3 = class        #4;       
const #4 = Asciz        java/lang/Object;    
const #5 = Asciz        <init>;  //实例构造器  
const #6 = Asciz        ()V;  //void返回类型  
const #7 = Asciz        Code;  //属性表Code属性  
const #8 = Method       #3.#9;  //方法特征签名  java/lang/Object."<init>":()V    
const #9 = NameAndType  #5:#6;//  方法名称和返回值"<init>":()V    
const #10 = Asciz       LineNumberTable;  //属性表源码行号和字节码指令对应表  
const #11 = Asciz       LocalVariableTable;  //属性表方法局部变量表  
const #12 = Asciz       this;  //Test类实例对象本身  
const #13 = Asciz       LTest;;  //对象类型,Test类  
const #14 = Asciz       getNum;  //方法名称  
const #15 = Asciz       (I)I;  //方法参数列表为一个int类型和返回值为int类型  
const #16 = Asciz       i;  //参数名称i  
const #17 = Asciz       I;  //参数类型int  
const #18 = Asciz       SourceFile;    
const #19 = Asciz       Test.java;    
 //方法表  
{    
//构造函数(默认构造方法)  
public Test();    
  Code:  //属性表Code属性  
   Stack=1, Locals=1, Args_size=1    
   0:   aload_0    
   1:   invokespecial   #8; //Method java/lang/Object."<init>":()V    
   4:   return    
  LineNumberTable:    
   line 2: 0    

  LocalVariableTable:  //属性表方法局部变量表  
   Start  Length  Slot  Name   Signature    
   0      5      0    this       LTest;    

//自定义方法  
public int getNum(int);    
  Code:    
   Stack=2, Locals=2, Args_size=2    
   0:   iload_1    
   1:   iconst_1    
   2:   iadd    
   3:   ireturn    
  LineNumberTable:    
   line 4: 0    

  LocalVariableTable:    
   Start  Length  Slot  Name   Signature    
   0      4      0    this       LTest;    
   0      4      1    i       I    
}  

原文地址:http://blog.csdn.net/chjttony/article/details/7907261

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值