javaClass类文件结构,类加载过程以及内存分配

知识要点:

Java虚拟机简介

Class类文件结构

Class类文件结构详解

Java虚拟机简介

Java虚拟机(JVM)是运行Java程序的抽象计算机,它是一种计算机设备的规范,可以采用不同的方式进行实现。Java程序通过运行在JVM中从而实现跨平台特性。
Java虚拟机不和包括Java在内的任何语言绑定,它只和Class文件这种特定的二进制文件格式关联,Class文件中包含了虚拟机指令集和符号表以及若干其他辅助信息。基于安全方面的考虑,Java虚拟机规范要求在Class文件中使用许多强制性的语法和结构化约束,但任一门功能性语言都可以表示为一个能被Java虚拟机所接受的有效Class文件。
问题:是不是只有Java编译器才能完成Java到class字节码文件的编译过程?


虚拟机产品

  • Sun HotSpot
  • BEA JRocket
  • IBM J9
  • Microsoft JVM
  • Google Android Dalvik

Class类文件结构

Class文件是一组以8位字节为基础单位的二进制流,各项数据项目严格按照顺序紧凑地排列在Class文件之中,中间没有添加任何分隔符,如果是超过8位字节以上空间的数据项,则会按照高位在前的方式(Big-Endian)分割成若干个8位字节进行存储。


魔数 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_class 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类文件组成

  • Class文件中包含:
  • Java虚拟机指令集
  • 符号表
  • 若干其他辅助信息
    Class文件中只有两种数据类型
  • 无符号数

  • 无符号数
    无符号数属于基本的数据类型,可用来描述数字,索引引用,数量值或者按照UTF-8编码构成的字符串值。无符号数据类型包含如下4种:
  • u1:1个字节
  • u2:2个字节
  • u4:4个字节
  • u8:8个字节

    表是由多个无符号数或者其他表作为数据项构成的复合数据类型,所有表都习惯性地以“_info”结尾。表用于描述由层次关系的复合结构的数据,整个Class文件本质上就是一张表

Class文件结构详解

常量池
Java代码在编译后,Class文件并不会保存各个方法、字段的最终内存布局,因为在编译时,Java类并不知道引用类的实际内存地址,因此只能使用符号引用来代替,而这些字段、方法引用不经过运行期转行就无法得到真正的内存地址入口。
当虚拟机运行式,需要从常量池获得对应的符号引用,而后再类创建或运行时解析成具体的内存地址,比如:在类加载器加载Test类时,此时可以通过虚拟机获取Test类的实际内存地址,因此便可以将符号ai.yunxi.Test替换为该类的实际内存地址。
符号引用包含三类常量:

  • 类和接口的全限定名, 如org.springframework.web.servlet.DispatherServlet
  • 字段的名称和描述符
  • 方法的名称和描述符
    常量池结构


    访问标志
    紧接着常量池之后的两个字节代表访问标志(access_flags),用于识别一些类或者接口层次的访问信息,包括:这个Class是类还是接口、是否为public类型、是否为abstract类型、类是否声明为final等。标志位及其含义如下表:
ACC_FINAL 0X0010 声明为final,只有类可以设置
ACC_SUPER 0X0020 使用invokespecial字节码指令的新语意,invokespecial指令的语意在JDK1.0.2发生过改变,为了区别这条指令使用哪种语意,JDK1.0.2之后编译出来的类都为真
ACC_INTERFACE 0X0200 接口
ACC_ABSTRACT 0X0400 abstract类型,对于接口或者抽象类来说,此标志值为真,其他类为假
ACC_SYNTHETIC 0X1000 这个类并非由用户代码产生
ACC_ANNOTATION 0X2000 注解
ACC_ENUM 0X4000 枚举

类父类与接口集合
访问标志之后顺序排列类索引(this)、父类索引(super)、接口索引集合(interfaces)。Class文件由这三项来确定这个类的集成关系。类索引和父类索引都是u2类型的数据。接口索引集合入口第一项是u2类型的接口计数器(interfaces_count)表示索引表的容量(即实现了几个接口)。如果该类没用实现任何接口,则计数器值为0,后面的接口索引表不再占用任何字节。
字段表集合
接口索引集合后边的是字段计数器:用于标识有多少个字段。字段计数器之后接着就是字段表集合。字段表(field_info)用于描述接口或者类中声明的变量。字段包括类级变量以及实例级变量。可以包括的信息有:

  • 字段的作用域(public、private、protected修饰符)
  • 实例变量还是类变量(static修饰符)
  • 可变性(final)
  • 并发可见性(volatile)
  • 可否被序列化(transient)
  • 字段数据类型(基本类型,对象,数组)
  • 字段名称
    字段表结构
u2 access_flags 1
u2 name_index 1
u2 descriptor_index 1
u2 attribute_count 1
attribute_info attributes attribute_count

字段访问标志

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 0X0100 字段是否由编译器自动产生的
ACC_ENUM 0X0400 字段是否enum

全限定名、简单名称及描述符

  • 全限定名
  • 简单名称:没有类型和参数修饰的方法或者字段名称,如:inc()和int m简单名称就是:inc、m
  • 描述符:用来描述字段的数据类型、方法的参数列表(数量、类型及顺序)和返回值
    方法表集合
    通过访问标志、名称索引、描述符索引可清楚的表达方法的定义,属性表是Class文件格式中最具扩展性的一种数据项目。
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 防范是否由编译器自动产生

属性表
Class文件、字段表、方法表、属性表都可以携带自己的属性表集合,用于描述某些场景专有的信息。属性表集合的限制稍微宽松,不再要求各个属性表具有严格顺序,只要不与已有属性名重复,任何人实现的编译器都可以向属性表中写入自己定义的属性信息。

Code 方法表 Java代码编译成的字节码指令
ConstantValue 字段表 final关键字定义的常量值
Deprecated 类、方法表、字段表 被声明为deprecated的方法和字段
Exceptions 方法表 方法抛出的异常
EnclosingMethod 类文件 仅当一个类为局部类或者匿名类时才能拥有这个属性,这个属性用于标识这个类所在的外围方法
InnerClasses 类文件 内部类列表
LineNumberTable Code属性 Java源码的行号与字节码指令的对应关系
LocalVariableTable Code属性 方法的局部变量描述
StackMapTable Code属性 JDK1.6中新增的属性,供新的类型检查验证器(Type Checker)检查和处理目标方法的局部变量和操作数栈所需要的类型是否匹配
SourceFile 类文件 记录源文件名称
Signature 类、方法表、字段表 JDK1.5中新增的属性,这个属性用于支持泛型情况下的方法签名,在Java语言中,任何类、接口、初始化方法或成员的泛型签名如果包含了类型变量(Type Variables)或参数化类型(Parameterized Types),则Signature属性会为它记录泛型签名信息。由于Java的泛型采用擦除法实现,在为了避免类型信息被擦除后导致签名混乱,需要这个属性记录泛型中的相关信息
SourceDebugExtension 类文件 JDK1.6中新增的属性,SourceDebugExtension属性用于存储额外的调试信息。譬如在进行JSP文件调试时,无法通过Java堆栈来定位JSP文件的行号,JSR-45规范为这些非Java语言编写,却需要编译成字节码并运行在Java虚拟机中的程序提供了一个进行调试的标准机制,使用SourceDebugExtension属性就可以用于存储这个标准所新加入的调试信息
Synthetic 类、方法表、字段表 标识方法或字段为编译器自动生成的
LocalVariableTypeTable JDK1.5中新增的属性,它使用特征签名代替描述符,是为了引入泛型语法之后能描述泛型参数化类型而添加
RuntimeVisibleAnnotations 类、方法表、字段表 JDK1.5新增的属性,为动态注解提供支持。RuntimeVisibleAnnotations属性用于注明哪些注解是运行时(实际上运行时就是进行反射调用)可见的
RuntimeInvisibleAnnotations 类、方法表、字段表 JDK1.5新增的属性,与RuntimeVisibleAnnotations属性作用刚好相反,用于指明哪些注解是运行时不可见的
RuntimeVisibleParameterAnnotations 方法表 JDK1.5新增的属性,作用与RuntimeVisibleAnnotations属性类似,只不过作用对象为方法参数
RuntimeInvisibleParameterAnnotations 方法表 JDK1.5新增的属性,作用与RuntimeInvisibleAnnotations属性类似,只不过作用对象为方法参数
AnnotationDefault 方法表 JDK1.5新增的属性,用于记录注解类元素的默认值
BootstrapMethods 类文件 JDK1.7中新增的属性,用于保存invokedynamic指令引用的引导方法限定符

属性表结构

u2 attribute_name_index 1
u4 attribute_length 1
u1 info attribute_length

Code属性表
Java程序方法体中的代码经过Javac编译处理后,最终变为字节码指令存储在Code属性中,Code属性出现在方法表的属性集合之中。但并非所有方法表都有Code属性,例如抽象类或接口。

u2 attribute_name_index 1
u4 attribute_length 1
u2 max_stack 1
u2 max_locals 1
u4 code_length 1
u1 code code_length
u2 exception_table_length 1
exception_info exception_table exception_table_length
u2 attribute_count
attribute_info attributes attribute_count

attribute_name_index:指向CONSTANT_Utf8_info类型常量的值固定为“Code”

  • attribute_length:属性值的总长度
  • max_stack:操作数栈(Operand Stacks)深度的最大值
  • max_locals:局部变量所需的存储空间(单位:Slot)
  • code_length:和code是用来存储Java源程序编译后产生的字节码指令
    异常属性表
start_pc u2 1
end_pc u2 1
handle_pc u2 1
catch_type u2 1

Exceptions属性表
Exceptions属性是在方法表中与Code属性平级的一项属性。Exceptions属性的作用是列举出方法中可能抛出的受查异常(Checked Exceptions), 也就是方法描述时在throws关键字后面列举的异常

attribute_name_index u2 1
attribute_length u4 1
number_of_exceptions u2 1
exception_index_table u2 number_of_exceptions

LineNumberTable属性表
LineNumberTable属性用于描述Java源码行号与字节码行号(字节码的偏移量)之间的对应关系。可以在编译的时候分别使用-g:none和-g:lines选项来取消或者要求生成这项信息。

attribute_name_index u2 1
attribute_length u4 1
line_number_table_length u2 1
line_number_table line_number_info line_number_table_length

LocalVariableTable属性表
LocalVariableTable属性用于描述栈帧中局部变量表中的变量与Java源码中定义的变量之间的关系。

attribute_name_index u2 1
attribute_length u4 1
local_varible_table_length u2 1
local_variable_table local_variable_info local_varible_table_length

variable_info
start_pc和length属性分别代表了这个局部变量的生命周期开始的字节码偏移量及其作用范围覆盖的长度,两者结合起来就是这个局部变量在字节码之中的作用域范围。name_index和descriptor_index都是指向常量池中CONSTANT_Utf8_info型常量的索引,分别代表了局部变量的名称及这个局部变量的描述符。index是这个局部变量在栈帧局部变量表中Slot的位置。当这个变量数据类型是64位类型时(double和long),它占用的Slot为index和index+1两个。

start_pc u2 1
length u2 1
name_index u2 1
descriptor_index u2 1
index u2 1

SourceFile
SourceFile属性用于记录生成这个Class文件的源码文件名称。sourcefile_index数据项是指向常量池中CONSTANT_Utf8_info型常量的索引,常量值是源码文件的文件名。

attribute_name_index u2 1
attribute_length u4 1
sourcefile_index u2 1

ConstantValue
ConstantValue属性的作用是通知虚拟机自动为静态变量赋值。只有被static关键字修饰的常量(类变量)才可以使用这项属性。目前Sun Javac编译器的选择是:如果同时使用final和static来修饰一个变量,并且这个变量的数据类型是基本类型或者java.lang.String的话,就生成ConstantValue属性来进行初始化,如果这个变量没有被final修饰,或者并非基本类型及字符串,则将会选择在<clinit>方法中进行初始化。

attribute_name_index u2 1
attribute_length u4 1
constantvalue_index u2 1

InnerClasses
InnerClasses属性用于记录内部类与宿主类之间的关联

attribute_name_index u2 1
attribute_length u4 1
number_of_classes u2 1
inner_classes inner_classes_info number_of_classes

number_of_classes代表需要记录多少个内部类信息。
inner_classes_info

inner_class_info_index u2 1
outer_class_info_index u2 1
inner_name_index u2 1
inner_class_access_flags u2 1

inner_class_info_index和outer_class_info_index都是指向常量池中CONSTANT_Class_info型常量的索引,分别代表了内部类和宿主类的符号引用。

  • inner_name_index代表内部类的名称
  • inner_class_access_flags是内部类的访问标志
    Deprecated和Synthetic
    Deprecated和Synthetic都属于标志类型的布尔属性,只存在有和没有的区别,没有属性值的概念。Deprecated代表已经不再推荐使用。Synthetic代表字段或者方法并不是有Java源码直接产生的,而是由编译器自行添加的。
attribute_name_index u2 1
attribute_length u2 1

StackMapTable
StackMapTable属性在JDK1.6发布后增加到了Class文件规范中,它是一个复杂的变长属性,位于Code属性的属性表中。会在虚拟机类加载的字节码验证阶段被新类型检查验证器(Type Checker)使用,目的在于代替以前比较消耗性能的基于数据流分析的类型推导验证器。一个方法的Code属性最多只能有一个StackMapTable属性。

attribute_name_index u2 1
attribute_length u4 1
number_of_entries u2 1
stack_map_frame stack_map_frame entries

注:近期和大家分享有关jvm相关的技术点,欢迎大家讨论学习!

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,456评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,370评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,337评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,583评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,596评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,572评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,936评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,595评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,850评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,601评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,685评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,371评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,951评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,934评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,167评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,636评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,411评论 2 342

推荐阅读更多精彩内容

  • 类文件结构 各种不同平台的虚拟机与所有平台都统一使用的程序存储格式— 字节码( ByteCode ) 是构成平台无...
    好好学习Sun阅读 516评论 0 0
  • 第6章类文件结构 6.1 概述 6.2 无关性基石 6.3 Class类文件的结构 java虚拟机不和包括java...
    kennethan阅读 905评论 0 2
  • 1.概述 write one, run everywhere。 2. 无关性的基石 实现语言无关性的基础是虚拟机和...
    过来摸摸头丶阅读 833评论 0 1
  • 代码编译的结果从本地机器码转变为字节码,是存储格式发展的一小步,确实编译语言发展的一大步。 虚拟机把描述类的数据从...
    胡二囧阅读 942评论 0 0
  • 正午的阳光直直地照射下来,在这初春的季节,终于积雪开始融化了。一路走来到处都可以听到潺潺流淌着的水声。老那牵着狗狗...
    我是空谷阅读 81评论 0 0