【阅读笔记】深入java虚拟机-第三部分-虚拟机执行子系统
首先,我不是书内容的搬运工,以下内容全部为个人的真实例子!
一个class片段
cafe babe 0000 0033 0148 0a00 4800 bc07
00bd 0a00 0200 bc08 0058 0b00 be00 bf0a
00c0 00c1 0a00 c200 c30a 0048 00c4 0a00
c500 c608 00c7 0a00 0c00 c807 00c9 0800
ca0a 00cb 00cc 0800 cd0a 00cb 00ce 0a00
c000 cf08 0071 0800 d00a 00d1 00d2 0800
6f08 0070 0900 4700 d30b 00d4 00d5 0800
d607 00d7 0a00 1a00 d80b 00d4 00d9 0b00
da00 db08 0087 0b00 dc00 dd08 0088 0a00
d100 de0a 00d1 00df 0800 e00a 00e1 00e2
0a00 d100 e30a 00e4 00e5 0500 0000 0000
0493 e00b 00dc 00e6 0800 e708 00e8 0b00
d400 e908 00ea 0800 eb08 00ec 0700 ed0a
0030 00bc 0900 4700 ee0b 00ef 00f0 0b00
ef00 f10b 00ef 00f2 0a00 4700 f30b 00dc
00f4 0a00 e400 f507 00f6 0a00 3900 bc08
00f7 0a00 3900 f808 00f9 0a00 3900 c408
00fa 0a00 c000 fb0a 00fc 00fd 0a00 3900
fe07 00ff 0a00 4300 bc0a 0043 0100 0a00
3901 0107 0102 0701 0301 000b 6958 4146
5365 7276 6963 6501 0027 4c6e 6574 2f73
6c77 6973 682f 717a 2f78 6166 2f73 6572
7669 6365 2f49 5841 4653 6572 7669 6365
3b01 0019 5275 6e74 696d 6556 6973 6962
6c65 416e 6e6f 7461 7469 6f6e 7301 0038
4c6f 7267 2f73 7072 696e 6766 7261 6d65
776f 726b 2f62 6561 6e73 2f66 6163 746f
7279 2f61 6e6e 6f74 6174 696f 6e2f 4175
746f 7769 7265 643b 0100 0b69 5765 6253
6572 6976 6365 0100 274c 6e65 742f 736c
魔数与class文件版本
魔数
所有class文件里的16进制都以cafe babe
开头
版本号
cafe babe
后面的0000
为次版本号,0033
为主版本号,转为10进制后,这个class的版本号为51.0
。
有时候项目启动会报
java.lang.UnsupportedClassVersionError: Unsupported major.minor version 51.0
,后面的51.0
也就是class的版本号。
有时候需要指定版本号编译,以maven项目为例
<properties>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
</properties>
常量池
引用书内说法
常量池中主要存放两大类常量:字面量(Literal)和符号引用(Symbolic References)。
字面量比较接近于Java语言层面的常量概念,如文本字符串、声明为final的常量值等。
而符号引用则属于编译原理方面的概念,包括了下面三类常量:类和接口的全限定名(Fully Qualified Name)、字段的名称和描述符(Descriptor)方法的名称和描述符
在本文最开始的class片段中
0148
表示常量池的数量,转换为10进制后,是328
,因为常量池的索引是从1
开始,也就是说有327
个常量。索引0
空出来有特殊的用途。
注:只有常量池的容量计数是从1开始,其他都是从0开始
常量池中14种常量项的结构总表
下次列出...
现在来分析一下上面的部分常量
0a 00 48 00 bc
0a => 10,类型为CONSTANT_Methodref_info
00 48 => 72,声明方法的类描述符CONSTANT_Class_info
的索引
00 bc => 188,名称级类型描述符CONSTANT_NameAndType
的索引
07 00 bd
07 => 7,类型为CONSTANT_Class_info
00 bd => 189,全限定名常量的索引
0a 00 02 00 bc
0a => 10,类型为CONSTANT_Methodref_info
00 02 => 2,声明方法的类描述符CONSTANT_Class_info
的索引
00 bc => 188,名称级类型描述符CONSTANT_NameAndType
的索引
08 00 58
08 => 8,类型为CONSTANT_String_info
00 58 => 88,字符串字面量的索引
etc...
- 0b 00 be 00 bf
- 0a 00 c0 00 c1
- 0a 00 c2 00 c3
- 0a 00 48 00 c4
- 0a 00 c5 00 c6
- 08 00 c7
- 0a 00 0c 00 c8
- 07 00 c9
- 08 00 ca
- 0a 00 cb 00 cc
- 08 00 cd
- 0a 00 cb 00 ce
- 0a 00 c0 00 cf
- 08 00 71
- 08 00 d0
- 0a 00 d1 00 d2
- ...
几个utf-8编码的字符串常量
- 01 00 0b 69 58 41 46 53 65 72 76 69 63 65
- 01 00 27 4c6e 6574 2f73 6c77 6973 682f 717a 2f78 6166 2f73 6572 7669 6365 2f49 5841 4653 6572 7669 6365 3b
注:这里用的是utf-8缩略码,即:
从'\u0001'到'\u007f'之间的字符(相当于1~127的ASCII码)的缩略编码使用一个
字节表示,
从'\u0080'到'\u07ff'之间的所有字符的缩略编码用两个字节表示,
从'\u0800'到'\uffff'之间的所有字符的缩略编码就按照普通UTF-8编码规则使用三个字节表示。
01 => 类型为CONSTANT_Utf8_info
00 0b => 长度为11的字符串
69 58 41 46 53 65 72 76 69 63 65 => '\u0069\u0058\u0041\u0046\u0053\u0065\u0072\u0076\u0069\u0063\u0065' => "iXAFService"
01 => 类型为CONSTANT_Utf8_info
00 27 => 长度为39的字符串
4c6e 6574 2f73 6c77 6973 682f 717a 2f78 6166 2f73 6572 7669 6365 2f49 5841 4653 6572 7669 6365 3b => '\u004c\u006e\u0065\u0074\u002f\u0073\u006c\u0077\u0069\u0073\u0068\u002f\u0071\u007a\u002f\u0078\u0061\u0066\u002f\u0073\u0065\u0072\u0076\u0069\u0063\u0065\u002f\u0049\u0058\u0041\u0046\u0053\u0065\u0072\u0076\u0069\u0063\u0065\u003b' => "Lnet/slwish/qz/xaf/service/IXAFService;"
问题:这里都是1个字节表示,那2个字节或者3个字节是怎么表示的呢?
猜测,如果不是1个字节,那就去javac编译时指定的编码,如果是utf-8,那就存储3个字节(取3个字节)
javac -encoding utf-8 Demo.java
要想看详细的常量池等其他信息,可以用javap -verbose Demo.class
查看。
访问标志
access_flags
访问标志表
下次列出...
例子
接口类型
public abstract class B {
// 代码省略...
}
编译后的文件就有这几种标志
ACC_PUBLIC
, ACC_SUPER
, ACC_ABSTRACT
那么,他的access_flags
就为0x0001|0x0020|0x0400=0x0421
枚举类型
public enum A {
// 代码省略...
}
编译后的文件就有这几种标志
ACC_PUBLIC
, ACC_FINAL
, ACC_SUPER
, ACC_ENUM
那么,他的access_flags
就为0x0001|0x0010|0x0020|0x4000=0x4031
类索引、父类索引与接口索引集合
名称 | 类型 |
---|---|
类索引(this_class) | u2 |
父类索引(super_class) | u2 |
接口索引集合(interfaces) | [u2, u2, ...] |
最近更新时间20200131
一是“诚”,
二是“勤”,
三是“专”。
当你无比地想做成一件事,
愿意为它倾尽无数心血和努力时,
结果总不会太差。