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

从常量池开始

常量池计数器后面紧跟着就是常量池的内容
所有的常量池项都具有如下通用格式:

cp_info
{
u1 tag;
u1 info[];
}
CONSTANT_Class_info
{
u1 tag;
u2 name_index;
}
CONSTANT_Fieldref_info
{
u1 tag;
u2 class_index;
u2 name_and_type_index;
}
CONSTANT_Methodref_info
{
u1 tag;
u2 class_index;
u2 name_and_type_index;
}
CONSTANT_InterfaceMethodref_info
{
u1 tag;
u2 class_index;
u2 name_and_type_index;
}
CONSTANT_NameAndType_info
{
u1 tag;
u2 name_index;
u2 descriptor_index;
}
CONSTANT_Utf8_info
{
u1 tag;
u2 length;
u1 bytes[length];
}
CONSTANT_MethodHandle_info
{
u1 tag;
u1 reference_kind;
u2 reference_index;
}


后面的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,接着开始第二个方法。

时间: 2024-08-31 22:53:44

JVM Class详解之一的相关文章

JVM Class详解之二 Method字节码指令

JVM Class详解之一中我们介绍了Class文件的结构和如何使用16进制编辑器读懂class文件. 今天我们来继续一起下Class文件中Method方法中经过java编译器编译后的Method字节码指令是什么样子的 JVM有哪些字节码指令 首先我们需要了解JVM有哪些字节码指令 第一类load类型 是将本地变量中的数据推送入栈中 (什么是本地变量我们后面聊) iload,iload_,lload,lload_,fload,fload_,dload,dload_,aload,aload_ il

Java中JVM虚拟机详解

1. 什么是JVM? JVM是Java Virtual Machine(Java虚拟机)的缩写,JVM是一种用于计算设备的规范,它是一个虚构出来的计算机,是通过在实际的计算机上仿真模拟各种计算机功能来实现的.Java虚拟机包括一套字节码指令集.一组寄存器.一个栈.一个垃圾回收堆和一个存储方法域. JVM屏蔽了与具体操作系统平台相关的信息,使Java程序只需生成在Java虚拟机上运行的目标代码(字节码),就可以在多种平台上不加修改地运行.JVM在执行字节码时,实际上最终还是把字节码解释成具体平台上

JVM的参数详解(转)

堆大小设置JVM 中最大堆大小有三方面限制:相关操作系统的数据模型(32-bt还是64-bit)限制:系统的可用虚拟内存限制:系统的可用物理内存限制.32位系统下,一般限制在1.5G~2G:64为操作系统对内存无限制.我在Windows Server 2003 系统,3.5G物理内存,JDK5.0下测试,最大可设置为1478m.典型设置:java -Xmx3550m -Xms3550m -Xmn2g -Xss128k-Xmx3550m:设置JVM最大可用内存为3550M.-Xms3550m:设置

JVM 使用 Akka 执行异步操作详解教程

如何通过以下方式实现并发性:     并行地在多个数据集上执行相同的操作(就像 Java 8 流一样)    显式地将计算构建成异步执行某些操作,然后将结果组合在一起(就像 future 一样). 这两种方法都是实现并发性的不错方式,但是您必须将它们明确地设计到应用程序中. 在本文和接下来的几篇文章中,我将着重介绍一种不同的并发性实现方法,该方法基于一种特定的程序结构,与显式编码方法不同.这种程序结构就是 actor 模型.您将了解如何使用 actor 模型的 Akka 实现.(Akka 是一个

JVM内存管理:垃圾搜集器详解

引言 在上一章我们已经探讨过hotspot上垃圾搜集器的实现,一共有六种实现六种组合.本次LZ与各位一起探讨下这六种搜集器各自的威力以及组合的威力如何. 为了方便各位的观看与对比,LZ决定采用当初写设计模式时使用的方式,针对某些搜集器,分几个维度去解释这些搜集器. client模式与server模式 在介绍本章内容之前,要说一下JVM的两种模式,一种是client模式,一种是server模式.我们平时开发使用的模式默认是client模式,也可以使用命令行参数-server强制开启server模式

Java虚拟机详解----JVM常见问题总结

[正文] 声明:本文只是做一个总结,有关jvm的详细知识可以参考本人之前的系列文章,尤其是那篇:Java虚拟机详解04----GC算法和种类.那篇文章和本文是面试时的重点. 面试必问关键词:JVM垃圾回收.类加载机制.   先把本文的目录画一个思维导图:(图的源文件在本文末尾)   一.Java引用的四种状态: 强引用: 用的最广.我们平时写代码时,new一个Object存放在堆内存,然后用一个引用指向它,这就是强引用. 如果一个对象具有强引用,那垃圾回收器绝不会回收它.当内存空间不足,Java

《Android游戏开发详解》一2.5 魔术揭秘——编译器和JVM

2.5 魔术揭秘--编译器和JVM Android游戏开发详解 在我们点击运行按钮和出现"Hello, world-?"之间,发生了什么事情.不管你是否相信,所有的事情都是在幕后进行的.当我们编写源代码的时候,Java编译器会编译它,这意味着,它会检查代码潜在的错误并将其转换为只有机器能够理解的语言.这个机器,就是执行代码并把想要的文本打印到控制台的Java虚拟机(Java Virtual Machine,JVM).如图2-18所示. JVM是一个虚拟的机器.它运行于操作系统之上,并且

《Android游戏开发详解》——第2章,第2.5节魔术揭秘——编译器和JVM

2.5 魔术揭秘--编译器和JVMAndroid游戏开发详解在我们点击运行按钮和出现"Hello, world-?"之间,发生了什么事情.不管你是否相信,所有的事情都是在幕后进行的.当我们编写源代码的时候,Java编译器会编译它,这意味着,它会检查代码潜在的错误并将其转换为只有机器能够理解的语言.这个机器,就是执行代码并把想要的文本打印到控制台的Java虚拟机(Java Virtual Machine,JVM).如图2-18所示. JVM是一个虚拟的机器.它运行于操作系统之上,并且能够

classpath详解(谨献给那些找不到北的朋友)

详解 设置类路径结构可通过对 JDK 工具使用 -classpath 选项(首选方法)或设置 CLASSPATH 环境变量来设置类路径.     C:> jdkTool -classpath path1;path2...    C:> set CLASSPATH=path1;path2...每个 path 以文件名或目录结尾,该文件名或目录取决于将类路径设置成什么: 对于包含 .class 文件的 .zip 或 .jar 文件,路径以 .zip 或 .jar 文件名结尾. 对于未命名包中的 .