`
zhtch_123
  • 浏览: 83328 次
  • 性别: Icon_minigender_1
  • 来自: 西安
社区版块
存档分类
最新评论

JVM学习序列之一:Java Class文件结构分析

 
阅读更多

Java Class文件结构分析

 

学习 Java 虚拟机对于理解 AOP ,反射,并发同步、垃圾回收、代码优化等方面都会有不少帮助,有时候还是有必要对底层的原理做一下了解。不过看起来确实比较费解。可以一步一步来,能看懂多少是多少。

Java 虚拟机可以从 class 字节码文件、类装载体系结构、对象生命周期、执行引擎、 API 规范等几个方面来学习。 Class 文件是 java 虚拟机的基础,从 class 文件结构中可以学习到 java 虚拟机的一些基本原理。

本文主要介绍 class 文件的设计结构,为后面的话题打下一个基础。 ( 主要参考了深入 java 虚拟机这本书和网上资料 )

1. ClassFile 基本定义

Classfile 是一个连续的 8 位字节二进制流,数据项按照顺序存储在 class 文件中,相邻项没有间隔,占多字节空间的项时,高位在前。

ClassFile 文件格式是固定的,按照顺序

名称

长度

描述

备注

majic

4 个字节

魔数 :0xCAFEBABE

Od –x 命令可以看到。这样保证了 Java 虚拟机能很轻松的分辨出 Java 文件和非 Java 文件

Minor_version major_version

分别 2 字节

主次版本号: Class 文件格式一旦发生变化,版本号也会随之变化。

如果 class 文件版本号超出了处理范围, java 虚拟机将不会处理该文件。

Constantpool_count,constanpool

不固定

常量池:包含了文件中类和接口相关的常量。文字字符串、 final 变量值、类名和方法名的常量。常量池的大均占到了整个类大小的 60% 左右。

入口列表的形式来存储。每个常量池入口都从一个长度为一个字节的标志开始。除了字面常量还可以容纳字段名称、方法名称和类的全限名等。

Access_flags

2 字节

访问标志 : 定义了类或接口

指明了是类还是接口、是抽象还是具体。公共、 final 等修饰符。

This_class

2 字节

本身是一个常量池的索引,指向了常量池中该类全限定名的常量池入口

Super_class

2 字节

指向父类全限定名

Interface_count interfaces

不固定

该类实现的接口数量, interfaces 包含了由该类实现的接口的常量池引用。

FiledsCount fileds

不固定

字段数量和字段的信息表。描述了字段的类型、描述符等。

Methods_count Mechods

不固定

方法总数和方法本身。使用 ASM 进行 AOP 编程,通常是通过调整 Method 中的指令来实现的。

每一个方法都会有一个 Mechod_info 表,改表记录了方法的方法名、描述符、返回类型。局部变量表,字节码序列等。

Attributes_count Attributes

不固定

属性总数和属性本身。写出了

2. CLassFile 详细结构

根据如上格式定义成结构体如下

( 以下内容参考了文章 http://hi.baidu.com/52dege/blog/item/f33a3cf4092491dbf3d3854f.html )

ClassFile {

u4 magic; // 必须为 : 0xCAFEBABE

u2 minor_version;

u2 major_version; //CLASS 文件结构主次版本号 JAVA2 支持 45.0-46.0

u2 constant_pool_count; // 记录常量信息

cp_info constant_pool[constant_pool_count-1]; // 计数从 1 开始

u2 access_flags; //class/interface 访问权限

u2 this_class; // 指向 constant_poll 中的有效索引值

u2 super_class; //0 或指向 constant_poll 中的有效索引值 , 对于 interface 必须为非 0

u2 interfaces_count; //superinterfaces 的个数

u2 interfaces[interfaces_count]; // 计数 [0,count-1) 对应 constant_pool 中的一个索引值

u2 fields_count;

field_info fields[fields_count]; // 主要用于记录 class 及实例中的变量

u2 methods_count;

method_info methods[methods_count];

u2 attributes_count;

attribute_info attributes[attributes_count];

}

对于上面的 cp_info constant_pool[constant_pool_count-1], 是一个 cp_info 结构体的系列。

cp_info {

u1 tag;

u1 info[];

}

java 虚拟机规范里面一共定义了 12 cp_info tag 值从 1 12

CONSTANT_Utf8 1

CONSTANT_Integer 3

CONSTANT_Float 4

CONSTANT_Long 5

CONSTANT_Double 6

CONSTANT_Class 7

CONSTANT_String 8

CONSTANT_Fieldref 9

CONSTANT_Methodref 10

CONSTANT_InterfaceMethodref 11

CONSTANT_NameAndType 12

( 好像没有 2 ???书上暂时也没看到。 )

对应的 cp_info 结构分别如下:

CONSTANT_Utf8

CONSTANT_Utf8_info {

u1 tag; //tag 值等于 1

u2 length;

u1 bytes[length];// 存储字符串数组

}

这是非常核心的一个 cp_info, 所有的字符串都由它存储表示。包括文字字符串、全限定名、字段名、方法名、描述符等都指向它。

CONSTANT_Integer

CONSTANT_Integer_info {

u1 tag; //tag 值等于 3

u4 bytes;// 按照高位在前格式存储 int 类型值

}

CONSTANT_Float

CONSTANT_Float_info {

u1 tag; //tag 值等于 4

u4 bytes;

}

CONSTANT_Long

CONSTANT_Long_info {

u1 tag; //tag 值等于 5

u4 high_bytes;

u4 low_bytes;

}

CONSTANT_Double

CONSTANT_Double_info {

u1 tag; //tag 值等于 6

u4 high_bytes;

u4 low_bytes

}

CONSTANT_Class ( 类全限定名 )

CONSTANT_Class_info {

u1 tag; //tag=7

u2 name_index; // 指向包含全限定名字符串的一个 CONSTANT_Utf8_info

}

CONSTANT_String

CONSTANT_String_info {

u1 tag; //tag=8

u2 string_index;// 包含文字字符串的 CONSTANT_Utf8_info 表索引

}

CONSTANT_Fieldref

CONSTANT_Fieldref_info {

u1 tag; //tag=9

u2 class_index; //constant_pool 的索引,对应 CONSTANT_Class_info

u2 name_and_type_index;//constant_pool 的索引,对应 CONSTANT_NameAndType_info

}

CONSTANT_Methodref

CONSTANT_Methodref_info {

u1 tag; //tag=10

u2 class_index;

u2 name_and_type_index;

}

CONSTANT_InterfaceMethodref

CONSTANT_InterfaceMethodref_info {

u1 tag; //tag=11

u2 class_index;

u2 name_and_type_index;

}

CONSTANT_NameAndType

CONSTANT_NameAndType_info {

u1 tag; //tag=12

u2 name_index;

u2 descriptor_index;

}

access_flags 意义如下 :

ACC_PUBLIC 0x0001

ACC_FINAL 0x0010

ACC_SUPER 0x0020

ACC_INTERFACE 0x0200

ACC_ABSTRACT 0x0400

this_class: constant_pool 中的索引值 , 指向的元素的 cp_info 等价为 CONSTANT_Class_info

CONSTANT_Class_info {

u1 tag; // 必须为 CONSTANT_Class (7)

u2 name_index; // 为指向 constant_pool 中的一个索引值

}

name_index : 指向的元素的 cp_info 等价为 CONSTANT_Utf8_info

field_info {

u2 access_flags; // 访问控制权

u2 name_index; //constant_pool 中的索引,对应于 CONSTANT_Utf8_info 描述。

u2 descriptor_index; //constant_pool 中的索引,对应于 CONSTANT_Utf8_info 描述。

u2 attributes_count;

attribute_info attributes[attributes_count]; //attribute_info 将在 mothods 后描述。

}

field_info access_flages 意义如下 :

ACC_PUBLIC 0x0001

ACC_PRIVATE 0x0002

ACC_PROTECTED 0x0004

ACC_STATIC 0x0008

ACC_FINAL 0x0010

ACC_VOLATILE 0x0040

ACC_TRANSIENT 0x0080

其中很显然不能同时为 ACC_FINAL ACC_VOLATILE ; 且前三项是互斥的。

interface 必须置 ACC_PUBLIC, ACC_STATIC,ACC_FINAL 位,且不能置其他位。

其他未指明的位保留将来使用,并且编译器应当将其置为 0 ,同时 Java 虚拟机应当忽略他们。

methods 指明了类中的所有方法。

method_info {

u2 access_flags;

u2 name_index; // 指向 constant_pool 的入口,对应为 CONSTANT_Utf8_info

u2 descriptor_index; // 指向 constant_pool 的入口,对应为 CONSTANT_Utf8_info

u2 attributes_count;

attribute_info attributes[attributes_count];

// 此处只能出现 Code Exceptions Synthetic Deprecated 四种类型的属性

}

access_flags 访问权描述如下 :

ACC_PUBLIC 0x0001

ACC_PRIVATE 0x0002

ACC_PROTECTED 0x0004

ACC_STATIC 0x0008

ACC_FINAL 0x0010

ACC_SYNCHRONIZED 0x0020

ACC_NATIVE 0x0100

ACC_ABSTRACT 0x0400

ACC_STRICT 0x0800

attribute_info {

u2 attribute_name_index; //constant_pool 中的索引,对应于 CONSTANT_Utf8_info 描述。

u4 attribute_length;

u1 info[attribute_length];

}

现在已经预定义的属性有 :

1. SourceFile : attribute_info 被替代为 :

SourceFile_attribute {

u2 attribute_name_index;

u4 attribute_length;

u2 sourcefile_index; // 指向 constant_pool 中的一个 CONSTANT_Utf8_info 结构。

}

2. ConstantValue : attribute_info 被替代为 :

ConstantValue_attribute {

u2 attribute_name_index;

u4 attribute_length; // 必须为 2

u2 constantvalue_index;

}

对于 constantvalue_index 意义如下 :

long CONSTANT_Long

float CONSTANT_Float

double CONSTANT_Double

int, short, char, byte, boolean CONSTANT_Integer

String CONSTANT_String

ConstantValue 用于 field_info 中,用于描述一个 static 常量 ,

且此时 field_info access_flags 应为 ACC_STATIC

3. Code : attribute_info 被替代为 :

Code_attribute {

u2 attribute_name_index;

u4 attribute_length;

u2 max_stack; // 执行此函数时可用的栈的最大深度

u2 max_locals; // 执行此函数可用到的最大本地变量数目,包括参数。

// 注意 : 一个 long/double 相当于 2 个变量数目 .

u4 code_length; // 本函数用到的代码长度。

u1 code[code_length]; // 实现本函数的真正字节码

u2 exception_table_length;

{ u2 start_pc;

u2 end_pc; // 捕获违例时执行代码数组中的 [start_pc, end_pc) 部分

u2 handler_pc; // 现在还不大明白他是干嘛的 !!

u2 catch_type; // 指向 constant_pool 的索引,对应 CONSTANT_Class_info

}exception_table[exception_table_length];

u2 attributes_count;

attribute_info attributes[attributes_count];

}

CONSTANT_Class_info {

u1 tag; // 必须为 CONSTANT_Class (7)

u2 name_index; // 不用我再说了吧 ?

}

Code 属性用于 method_info 结构中。

4. Exceptions : attribute_info 被替代为 :

Exceptions_attribute {

u2 attribute_name_index;

u4 attribute_length;

u2 number_of_exceptions;

u2 exception_index_table[number_of_exceptions];

}

5. InnerClasses : attribute_info 被替代为 :

InnerClasses_attribute {

u2 attribute_name_index;

u4 attribute_length;

u2 number_of_classes;

{ u2 inner_class_info_index;

u2 outer_class_info_index;

u2 inner_name_index;

u2 inner_class_access_flags;

} classes[number_of_classes];

}

6. Synthetic : attribute_info 被替代为 :

Synthetic_attribute {

u2 attribute_name_index; // 不用废话了吧 ?

u4 attribute_length; // 必须为 0

}

Synthetic 用在 field_info method_info 中,

一个没有出现在源程序中的变量必须使用 Synthetic 标记。

7. LineNumberTable : attribute_info 被替代为 :

LineNumberTable_attribute {

u2 attribute_name_index;

u4 attribute_length;

u2 line_number_table_length;

{ u2 start_pc; // 代码数组中的开始处

u2 line_number; // 源文件中的行号 ( 对于每一非空行都有这么一项 )

} line_number_table[line_number_table_length];

}

LineNumberTable 用于 Code 属性中,通常用于调试。

8. LocalVariableTable : attribute_info 被替代为 :

LocalVariableTable_attribute {

u2 attribute_name_index;

u4 attribute_length;

u2 local_variable_table_length;

{ u2 start_pc;

u2 length; // 当解释到代码数组的 [start_pc,start_pc+length]

// 时变量必须被赋值 ??

u2 name_index;

u2 descriptor_index;

u2 index; // 到本地变量数组的一个索引

} local_variable_table[local_variable_table_length];

}

9. Deprecated : attribute_info 被替代为 :

Deprecated_attribute {

u2 attribute_name_index;

u4 attribute_length; // 必须为 0

}

当然也可以定义自己的属性,但要你自己的编译器和虚拟机实现。 JVM 将忽略自己不认可的属性。

以上是关于 Javaclass 文件的一个基本结构的分析。主要是对书上关于这一章的总结,同时参考了 : http://hi.baidu.com/52dege/blog/item/f33a3cf4092491dbf3d3854f.html 等网上资料。

 


看一个 java classfile 有助于我们理解的实际例子:

 


代码 1


运行结果为 :true true

代码 2


运行结果为 :false true

关于代码 1 和代码 2 的解释:

Java 编译器在生成 class 文件的时候对代码里面定义的字符串都会作为一个字符串常量存放在常量池中。

对于代码 1 中的 String a = "aaabbb"; String b = "aaabbb"; 预处理在编译处理后, a b 都指向了常量池里的 ”aaabbb” ,所以 a==b true.

对于代码 2 中的 String b = "aaa"; b += "bbb"; 编译处理后会在常量池里面生成 ”aaa” ”bbb”, 同时, b += "bbb"; 语句会导致 Jvm 去重新创建一个 string 对象,相当于 b=new String(“aaabbb”); 自然, aaa==bbb false 。这也是为什么不推荐使用加号来拼接字符串了。

 

 

分享到:
评论

相关推荐

    jvm解析编译过的class文件

    另外,` jclasslib`是一款可视化的字节码查看器,它提供了更直观的方式查看和分析.class文件,包括类结构、常量池、字段和方法的详细信息。使用这样的工具可以帮助我们更好地理解JVM的内部工作原理。 此外,学习...

    java class文件编辑器

    Java Class文件编辑器,如JD-GUI 0.3.3 绿色版,是一种用于查看和理解Java字节码的工具,对于开发者而言尤其重要,因为它提供了将已编译的`.class`文件反编译回源代码的能力。在深入讨论这个工具之前,我们先来了解...

    Jvm之用C#解析class文件

    总的来说,使用C#解析Java的class文件是一项挑战性的任务,它需要对Java字节码格式有深入理解,并熟练掌握C#的IO和数据结构处理。通过这样的实践,开发者不仅可以提升自己的跨语言编程技能,还能增进对Java字节码和...

    java class文件反编译工具

    标题提到的"java class文件反编译工具"主要指的是一类能够解析和解释Java字节码的软件。在Java平台上,源代码被编译成字节码,这是一种中间语言,可以在任何支持Java虚拟机(JVM)的设备上运行。然而,字节码是机器...

    反编译软件,可以将class文件反编译为java文件

    在IT领域,反编译是一种将已编译的字节码(如Java的.class文件)转换回源代码(如Java的.java文件)的过程。标题提到的"反编译软件"是一种工具,它允许用户查看和理解Java类文件的内部结构,即使原始的源代码不再...

    java class文件反编工具

    Java Class文件是Java程序在编译后生成的字节码文件,它包含了程序的结构和指令,用于JVM(Java虚拟机)执行。反编译工具则是将这些字节码转换回可读的源代码,帮助开发者理解已编译的程序逻辑,进行代码调试、学习...

    Java class反编译文件

    Java类文件(.class)是Java源代码经过编译后的二进制文件,它包含了机器码,用于JVM(Java虚拟机)执行。反编译是将这些二进制.class文件转换回接近原始的Java源代码(.java文件)的过程。这个过程对于理解已编译...

    javaclass反编辑器

    JavaClass反编辑器是一款专门用于分析和理解Java字节码的工具,它允许开发者查看、解构和理解编译后的Java类文件。在Java应用程序的开发、调试和逆向工程中,这类工具扮演了重要角色。Java类文件是Java源代码经过...

    class文件查看器.rar

    今天我们将探讨一款名为"class文件查看器"的工具,它是一个跨平台的实用程序,专为查看和分析Java class文件而设计。通过这个工具,开发者能够直观地查看class文件中的指令以及16进制字节码表示,极大地增强了代码...

    JAVA-class编辑器

    总的来说,JAVA-class编辑器是Java开发者的重要工具之一,它提供了一种途径去探索、理解和修改Java字节码,从而提升开发效率,优化程序性能,并进行深度学习。了解和掌握这类工具的使用,对于提升开发者的技术水平和...

    JavaClassViewer

    JavaClassViewer是一款专为开发者设计的工具,用于查看和理解`.class`文件的内部结构。`.class`文件是Java编译后的二进制文件,它包含了程序的字节码,这是Java虚拟机(JVM)运行的基础。通过使用JavaClassViewer,...

    从Java代码到字节码(1)Java开发Java经验技巧共

    1. **Java编译过程**:当开发者编写完.java文件后,javac编译器会进行词法分析、语法分析、语义分析等步骤,将高级语言转化为机器可以理解的字节码指令,这些指令存储在.class文件中。每个类都对应一个.class文件,...

    java源码:JAVA字节码操作库 BCEL.zip

    Java字节码操作库BCEL(Byte Code Engineering Library)是一个强大的工具,用于分析、修改和创建Java类文件。它是Apache软件基金会的Jakarta项目的一部分,为开发者提供了对字节码的底层控制,使得开发者能够在运行...

    什么是Java的序列化和反序列化?如何实现对象的序列化和反序列化?(java面试题附答案).txt

    ### Java中的序列化与反序列化详解 #### 一、概念理解 ...通过上述分析,我们可以看到Java中的序列化与反序列化机制是非常实用且强大的。掌握这些技术可以帮助开发者更好地处理对象的存储与传输问题。

    Java对象的序列化和反序列化实践

    例如,`ObjectSaver.java`文件可能包含一个实现了序列化的类定义: ```java public class MySerializableObject implements Serializable { private String name; private int age; // 构造器,getter和setter...

    圣思园张龙 深入理解jvm

    Java虚拟机(JVM)是一种能够执行字节码(.class文件)的虚拟机。它是Java平台的核心部分之一,使得Java应用程序可以在任何支持JVM的平台上运行,而无需考虑底层操作系统的差异性。JVM的主要职责包括: - **加载代码...

    Java序列化和反序列化

    3. **远程方法调用(RMI)**:在分布式计算环境中,序列化允许对象跨越网络边界,实现在不同机器间的对象交换,是RMI的基础技术之一。 #### 序列化的过程剖析 序列化过程中,对象的实例变量及其值被转换为可存储或...

    Java 编程 源码 处理

    1. **编译与解释**:Java代码首先需要通过JDK(Java Development Kit)中的javac编译器转化为字节码(.class文件),然后由Java虚拟机(JVM)执行。了解这个过程有助于优化代码性能和理解异常处理。 2. **面向对象...

Global site tag (gtag.js) - Google Analytics