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

JVM源码分析之不保证顺序的Class.getMethods 概述 本文要说的内容是今天公司有个线上系统踩了一个坑,并且貌似还造成了一定的影响,后来系统相关的人定位到了是java.lang.Class.getMethods返回的顺序可能不同机器不一样,有问题的机器和没问题的机器这个返回的方法列表是不一样的,后面他们就来找到我求证是否jdk里有这潜规则 本来这个问题简单一句话就可以说明白,所以在晚上推送的消息里也将这个事实告诉了大家,大家知道就好,以后不要再掉... 阅读详情

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 。这也是为什么不推荐使用加号来拼接字符串了。

 

字节码文件(Class文件)结构解读、理解与分析 字节码 什么是字节码? 字节码(Byte-code)是一种包含执行程序,由一序列 op 代码/数据对组成的二进制文件,是一种中间码。字节是电脑里的数据量单位。 字节码文件展示 魔数(4个字节 ) 每个Class文件的头4个字节被称为魔数(Magic Number) , 值为0xCAFEBABE(咖啡宝贝? ),它的唯一作用是确定这个文件是否为一个能被虚拟机接受的Class文件。 版本号(4个字节) 紧接着魔数的4个字节存储的是Class文件的版本号: 第5和第6个字节是次版本号(Min 阅读详情

相关推荐

java-13: 面向切面编程AOP

本文对知乎上的面向切面编程AOP做了一个两篇文章的合集,包括理解和实例。 第一部分:AOP的简单理解。来自知乎。 第二部分:AOP的深入理解。来自知乎。 第三部分:AOP的实例。来自博客园。地址:https://www.cnblogs.com/xrq730/p/4919025.html 知乎连接:https://www.zhihu.com/question/24863332,问题:什么是面...

xushaozhang的专栏 421

使用 MethodInfo 调用带参数的方法

参考来自链接https://www.cnblogs.com/CodingHiding/articles/3328362.html using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.L...

dd_zhouqian的专栏 5090

Java Class文件结构实例分析(上)

发表文章之后,发现很多图片显示不了,请阅读我的公众号文章,以获得本文最佳体验: Java Class文件结构实例分析(上)   本文假定读者对Java Class文件格式有一些基本的了解,建议结合相关书籍进行对照阅读。 Class文件格式信息 实例代码 package chapter6;public class TestClass {    private int m;    ...

二进制之路 250

数据结构八种基本序列代码----堆序列

package sorting; import java.util.Arrays; public class HeapSort {//堆 public static void main(String[] args) { int[] a={49,38,65,97,76,13,27,49,78,34,12,64}; in...

qq_42866384的博客 1129

JAVA锁原理和选择synchronized & Lock

锁 废话不多说,进正题,参考一下synchronized 的实现原理 synchronized 的实现原理 JVM可以从方法常量池中的方法表结构(method_info Structure) 中的 ACC_SYNCHRONIZED 访问标志区分一个方法是否同步方法。当方法调用时,调用指令将会 检查方法的 ACC_SYNCHRONIZED 访问标志是否被设置,如果设置了,执行线程将先持有mon...

呼吸吐纳的博客 381

Java 入门指南:JVMJava虚拟机)—— Java文件结构

Java 中,JVM 可以理解的代码是字节码(即扩展名为 .class 的文件),它不面向任何特定的处理器,只面向虚拟机。Java 语言通过字节码的方式,在一定程度上解决了传统解释型语言执行效率低的问题,同时又保留了解释型语言可移植的特点。 .class 文件是不同的语言在 Java 虚拟机之间的重要桥梁,同时也是支持 Java 跨平台很重要的一个原因。根据 Java 虚拟机规范,Class 文件通过 ClassFile 定义,有点类似 C 语言的结构体。

热带鱼的技术博客 1498

JVM源码研读:章节序列

Java 虚拟机基础 JVM源码系列:JVM内部运行之Class的Method Java (JDK7)中的String常量和String.intern的实现 JVM源码系列:ThreadMXBean 打出堆栈信息原理分析 JVM源码系列:使用PrintAssembly打印java运行过程中的汇编 JVM源码系列:java如何实现多态性,基于itable, vtable源码分析 JVM源...

沧海一粟 4203

java class jvm_[Java教程]JVMClass文件

[Java教程]JVMClass文件0 2016-11-25 18:02:39单纯的看 JVM 规范有点无聊了,看的云里雾里的,所谓“不闻不若闻之,闻之不若见之,见之不若知之,知之不若行之,学至于行而止矣,行之,明也”。本文从一个简单的Java程序一步一步探讨Class文件的结构。先从简单的 Hello World 开始,使用 javac 和 javap命令进行编译和反编译,由于反...

weixin_28713299的博客 178

JVM-Class文件结构

1 Class文件结构 2 虚拟机类加载机制 3 类加载器 4 运行时数据区 5 垃圾回收机制 6 内存分配与回收策略 7 字节码执行引擎 8 JDK监控管理工具 9 JVM调优实战 10 总结 11 JVM最重要的第一课Java Class文件解析 12 作为一个Java工程师你必须要知道的GC垃圾回收 Java文件结构 问题:其他语言是否可以使用Java虚拟机作为产品交付媒介 问题1:是不...

hongxue8888的博客 316

JVMClass对象的存储位置和作用

JVMClass对象的存储位置和作用 (2018-03-25 16:50:16) 分类: Java 在弄清这个问题前必须的明白类型信息和JVM方法区。 类型信息是一个java类的描述信息(class mate),classloader加载一个类时从class文件中提取出来并存储在方法区。它包括以下信息: 1.类型的完整有效名,类型的修饰符(public,abstract, final的某个子集)...

joker_cc的博客 1万+

Java中每个类都有个Class对象,那Class类有自己的Class对象吗?

JavaClass 类是一个强大的工具,它不仅为每个类提供了元数据,而且自身也遵循了这一规则,拥有自己的 Class 对象。下次当你在使用反射或者处理类相关的操作时,不妨想一想这个 Class 类的 Class 对象,它可能会给你带来新的编程灵感。这个 Class 对象,就像是每个类的身份证,记录着类的名字、属性、方法等信息。这意味着,Class 类自身也是一个类,按照 Java 的规则,它也应该有一个 Class 对象。是的,你没看错,Class 类有一个自己的 Class 对象,它就是。

龚小帅的博客 887

理解JVMclass文件结构

理解JVMclass文件结构         开发者将Java代码写在.java文件中,经过编译器的编译生成了class文件,原来在Java文件中定义的信息将转存到class文件中。只有经过编译后的class文件才能被虚拟机理解,JVM才能将文件中的内容转换成其自己的内存结构。这个过程中,开发者和jvm好比不能沟通的两个人,但是开发者可以通过翻译(编译器)将他自己才懂得Java翻译成cla

hongxingxiaonan的专栏 699

JVM学习笔记之CLASS结构和动态链接模型

之前分析了方法调用和运行的过程,基本上可以满足代码层的需求了,不过为了更好的理解调用和运行的过程,还是分析CLASS文件的结构和他动态链接的模型。     首先还是得看下CLASS文件的结构。CLASS文件的结构分为(只列了简单的总结,我目前也用不到太深入的):

落月星辰的空间 577

JVM调优之class文件结构

大家应该知道,并不是只有java编译器才能生成class字节码文件,如下图所示,Jruby程序可以通过Jruby编译器、Groovy程序可以通过Groovy编译器生成可在java虚拟机上运行的class文件。 class文件组成内容 class文件包含虚拟机指令,符号表和其他辅助信息。有两种数据结构无符号数和表。 什么是u1 u2 u4 u8  u1一个字节;  u2二个字节;  u4...

nx1142041660的博客 170

如何通过反编译 JVM class 文件来学习 Java 和 Kotlin

学习 JVM 指令,能看懂 class 文件,对于理解基于 JVM 的语言,如 Java 和 Kotlin 都非常有帮助,通过阅读编译后的 class 文件,我们可以看到 what is under the hook。 解析 首先,对于一个编译得到的 xxx.class 文件,通过 javap 指令可以进行解析。 常用的解析命令是: javap -v -p xxx # javap -h 可以提示各种参数的意思 # -p 是显示 private 成员,-v 是 verbose 输出附加信息 阅读 解析得到

哈罗德Android笔记 524
上一篇: 实例学习maven 项目构建
下一篇: DbUnit 简介和使用 (puts your database into a known state)
abing37
博客等级 码龄21年 34粉丝 25原创
评论 3
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值