Java的class字节码结构

情景

问:Java中的String的字符串长度有限吗?

答:我知道茴的四种写法,你看啊。。。。

问:。。。。

探索

为了了解这问题,我们需要区探究一下class文件

class文件又叫字节码文件,是它为java实现了跨平台运行的能力。

字节码也解除了虚拟机和java之间的耦合,因为java虚拟机可以支持其他语言上生成的字节码,例如JRuby,Groovy。

从纵观角度来看,class文件只有两种数据结构:无符号数

  • 无符号数:属于基本的数据类型,以u1,u2,u4,u8来分别代表1字节2字节4字节8字节的无符号数,他可以用来描述数字、索引引用、数值或者UTF-8字符串编码。
  • :表是由多个无符号数或者其他表结构组合的复合数据类型,class文件中的所有的表结构都以_info结尾。其实class文件就是一张表。

这些结构按照预先规定好的顺序紧密的相连,结构顺序如下所示:

当jvm加载某个class文件时,jvm就是根据上图的结构区解析class文件,并加载到内存中,并根据下图的情况分配内存空间。

用实例分析

首先编写一段简单的java代码

import java.io.Serializable;
public class Test implements Serializable, Cloneable{
      private int num = 1;
      public int add(int i) {
          int j = 10;
          num = num + i;
          return num;
     }
}

然后通过javac生成Test.class,然后用16进制编译器打开:

  • 魔数:开头的四位 ca fe ba be,他是固定值,用来判断是否标准class文件
  • 版本号00 00 00 34,前两位代表次版本号(minor_version),后两位代表主版本号(major_version)。说明当前的版本号是52.0对应jdk1.8.0
  • 常量池:它是一个叫做常量池的表(cp_info),在常量池中保存了各种类的信息,比如类的名称、父类、方法、参数类型等。

常量池中的每一个类项都是一个表,共有14种类型,如下:

常量池中的每一项都会有一个u1大小的tag值,用于标记当前数据结构属于哪一种表。

我们以CONSTANT_Class_infoCONSTANT_Utf8_info两张表举例说明:

table CONSTANT_Class_info {
    u1  tag = 7;
    u2  name_index;
}
  • tag:占用一个字节大小。比如值为 7,说明是 CONSTANT_Class_info 类型表。
  • name_index:是一个索引值,可以将它理解为一个指针,指向常量池中索引为 name_index 的常量表。比如 name_index = 2,则它指向常量池中第 2 个常量。

接下来我们来看看CONSTANT_Utf8_info的表结构:

table CONSTANT_utf8_info {
    u1  tag;
    u2  length;
    u1[] bytes;
}
  • tag:值为1 ,表示是CONSTANT_Utf8_info
  • length:表示u1[]数组的长度,例如length=5,则表示接下来5个连续的u1类型数据
  • bytes:u1 类型数组,长度为上面第 2 个参数 length 的值。

我们java代码声明的String字符串最终的存储格式就是CONSTANT_Utf8_info,因此length最大能表示的长度就是u2能代表的最大值65536个,但是需要额外的两个字节来保存null值,因此String所能表示的最大长度是65536-2=65534

不难看出常量池内部的表中也有相互之间的引用,用一张图来表示CONSTANT_Class_infoCONSTANT_Utf8_info表格间的关系

理解了常量池内部的数据结构之后,我们看一下实例代码解析过程。从版本号之后开始解析:

  • 00 1d:说明常量计数器的值为29,由于下标为0的常量被JVM使用,我们实际上常量池的大小为28.

  • ** 0a**:可以查到表类型为CONSTANT_Methodref_info,因此常量池中的第一个常量类型为方法引用表。其结构如下:

CONSTANT_Methodref_info {
    u1 tag = 10;
    u2 class_index;        指向此方法的所属类
    u2 name_type_index;    指向此方法的名称和类型

}
  • 00 06:指向常量池中的第6个常量
  • 00 15:指向常量池中的第21个常量

第一个表已经解读完了,接下来时第二个表

  • 09:当前时字段引用表,结构如下
CONSTANT_Fieldref_info{
    u1 tag;
    u2 class_index;        指向此字段的所属类
    u2 name_type_index;    指向此字段的名称和类型
}
  • 00 05 :指向常量池中第 5 个常量。
  • 00 16:指向常量池中第 22 个常量。

我们已经解析了常量池中的两个常量, 后面的常量解析方法如出一辙,实际上我们可以借助javap命令来查看常量池中的内容

javap -v Test.class

其结果正如我们前面解析的一样,其中下标为21的常量类型为NameAndType,它的数据结构是

CONSTANT_NameAndType_info{
    u1 tag;
    u2 name_index;    指向某字段或方法的名称字符串
    u2 type_index;    指向某字段或方法的类型字符串

}
  • 而下标在 21 的 NameAndTypename_indextype_index 分别指向了 13 和 14,也就是“<init>”“()V”。因此最终解析下来常量池中第 1 个常量的解析过程以及最终值如下图所示:

经过仔细分析,我们可以知道常量池中第一个常量保存的是Object中的默认构造方法。

  • 常量池之后,是访问标志00 21,它占两个字节,前面的表中有白框标识:它代表了类或者方法的修辞方式,含义如下图所示。
  • 00 05:类索引
  • 00 06: 父类索引
  • 00 02 :接口索引计数器

回顾常量池

类名是Test,父类是Object,我们接着看接口计数器为2 说明下面的4个字节描述了两个接口

  • 00 07:常量值为"Serializable"
  • 00 08 :常量值为"Cloneable"

综上:当前类为 Test 继承自 Object 类,并实现了“Serializable”和“Cloneable”这两个接口。

接下里是字段表

  • 00 02 :字段计数器:表示类中声明类两个字段,接着回出现两个字段表的数据结构,字段表数据结构如下:
CONSTANT_Fieldref_info{
    u2  access_flags    字段的访问标志
    u2  name_index          字段的名称索引(也就是变量名)
    u2  descriptor_index    字段的描述索引(也就是变量的类型)
    u2  attributes_count    属性计数器
    attribute_info
}
  • 00 02:字段访问标志,代表是private类型,解析如下图所示:
  • 00 09:变量名索引,变量名是num
  • 00 0a:变量类型,类型是I,说明是int类型变量

接下来的解析如出一辙,我们说一下注意事项。

  1. 字段表集合中不会列出从父类或者父接口中继承而来的字段。

  2. 内部类中为了保持对外部类的访问性,会自动添加指向外部类实例的字段。

方法表

方法表紧随字段表其后,也是从一个计数器开始

方法表的结构如下:

CONSTANT_Methodref_info{
    u2  access_flags;        方法的访问标志
    u2  name_index;          指向方法名的索引
    u2  descriptor_index;    指向方法类型的索引
    u2  attributes_count;    方法属性计数器
    attribute_info attributes;
}

访问标志的值如下:

第一个方法是构造方法,我们主要分析一下add方法:

从图中我们可以看出 add 方法的以下字段的具体值:

  1. access_flags = 00 01 也就是访问权限为 public。
  2. name_index = 00 11 指向常量池中的第 17 个常量,也就是“add”。
  3. type_index = 00 12 指向常量池中的第 18 个常量,也即是 (I)。这个方法接收 int 类型参数,并返回 int 类型参数。

属性表:在之前解析字段和方法的时候,在它们的具体结构中我们都能看到有一个叫作 attributes_info 的表,这就是属性表。

属性表并没有一个固定的结构,各种不同的属性只要满足以下结构即可:

CONSTANT_Attribute_info{
    u2 name_index;
    u2 attribute_length length;
    u1[] info;
}

我们接着往下看:

  • 00 01:属性计数器,说明只有一个属性
  • 00 0f:属性索引,通过查看常量,我们可以看出它是code属性表
code属性表中,最主要的就是一系列的字节码。通过`javap -v Test.class`我们可以查看到方法的字节码

JVM执行add方法时,就是通过这一系列的指令操作来完成的。

解答

String能保存的最大长度需要从两个角度来回答,在编译期还是运行期。

编译期由于是CONSTANT_Utf8_info格式存储的,所以最大长度是65534字节,这里需要注意,英文和数字是占用1字节,而汉字是占用两个字节,所以不一定能存到最大长度的字符。

运行期时字符串的内部是有char数组的value来存储的,数组的长度表示类型是int类型,所以这时候String的最大长度是Integer.MAX_VALUE (2147483647)了,大约运行时需要4BG内存才能达到最大最大字符串长度。

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

推荐阅读更多精彩内容