56 - ASM之ClassFile和Instruction

ClassFile对方法的约束

从ClassFile的角度来说,它对于方法接收的参数数量、方法体的大小做了约束。

方法参数的数量(255)

在一个方法当中,方法接收的参数最多有255个。我们分成三种情况来进行说明:

  • 第一种情况,对于non-static方法来说,this也要占用1个参数位置,因此接收的参数最多有254个参数。
  • 第二种情况,对于static方法来说,不需要存储this变量,因此接收的参数最多有255个参数。
  • 第三种情况,不管是non-static方法,还是static方法,long类型或double类型占据2个参数位置,所以实际的参数数量要小于255。
public class HelloWorld {
    public void test(int a, int b) {
        // do nothing
    }

    public static void main(String[] args) {
        for (int i = 1; i <= 255; i++) {
            String item = String.format("int val%d,", i);
            System.out.println(item);
        }
    }
}

Java Virtual Machine Specification4.11. Limitations of the Java Virtual Machine部分对方法参数的数量进行了限制:

The number of method parameters is limited to 255 by the definition of a method descriptor, where the limit includes one unit for this in the case of instance or interface method invocations.

方法体的大小(65535)

对于方法来说,方法体并不是想写多少代码就写多少代码,它的大小也有一个限制:方法体内最多包含65535个字节。

当方法体的代码超过65535字节(bytes)时,会出现编译错误:code too large。
Java Virtual Machine Specification4.7.3. The Code Attribute部分定义了Code属性:

Code_attribute {
    u2 attribute_name_index;
    u4 attribute_length;
    u2 max_stack;
    u2 max_locals;
    u4 code_length;
    u1 code[code_length];
    u2 exception_table_length;
    {   u2 start_pc;
        u2 end_pc;
        u2 handler_pc;
        u2 catch_type;
    } exception_table[exception_table_length];
    u2 attributes_count;
    attribute_info attributes[attributes_count];
}

code_length: The value of the code_length item gives the number of bytes in the code array for this method. The value of code_length must be greater than zero (as the code array must not be empty) and less than 65536.

Instruction VS. Opcode

严格的来说,instruction和opcode这两个概念是有区别的。相对来说,instruction是一个比较大的概念,而opcode是一个比较小的概念:

instruction = opcode + operands

A Java Virtual Machine instruction consists of a one-byte opcode specifying the operation to be performed, followed by zero or more operands supplying arguments or data that are used by the operation. Many instructions have no operands and consist only of an opcode.
参考: 2.11. Instruction Set Summary

Opcodes

  • 一个字节的容量(256)

    • opcode占用空间的大小为1 byte。在1 byte中,包含8个bit,因此1 byte最多可以表示256个值(即0~255)。
  • opcode的数量(205)

    • 虽然一个字节(byte)当中可以存储256个值(即0~255),但是在Java 8这个版本中定义的opcode数量只有205个。因此,opcode的内容就是一个“数值”,位于0~255之间。
  • opcode和mnemonic symbol

    • 要记住每个opcode对应数值的含义,是非常困难的。为了方便人们记忆opcode的作用,就给每个opcode起了一个名字,叫作mnemonic symbol(助记符号)。
opcode mnemonic symbol opcode mnemonic symbol opcode mnemonic symbol opcode mnemonic symbol
0 nop 64 lstore_1 128 ior 192 checkcast
1 aconst_null 65 lstore_2 129 lor 193 instanceof
2 iconst_m1 66 lstore_3 130 ixor 194 monitorenter
3 iconst_0 67 fstore_0 131 lxor 195 monitorexit
4 iconst_1 68 fstore_1 132 iinc 196 wide
5 iconst_2 69 fstore_2 133 i2l 197 multianewarray
6 iconst_3 70 fstore_3 134 i2f 198 ifnull
7 iconst_4 71 dstore_0 135 i2d 199 ifnonnull
8 iconst_5 72 dstore_1 136 l2i 200 goto_w
9 lconst_0 73 dstore_2 137 l2f 201 jsr_w
10 lconst_1 74 dstore_3 138 l2d 202 breakpoint
11 fconst_0 75 astore_0 139 f2i 203
12 fconst_1 76 astore_1 140 f2l 204
13 fconst_2 77 astore_2 141 f2d 205
14 dconst_0 78 astore_3 142 d2i 206
15 dconst_1 79 iastore 143 d2l 207
16 bipush 80 lastore 144 d2f 208
17 sipush 81 fastore 145 i2b 209
18 ldc 82 dastore 146 i2c 210
19 ldc_w 83 aastore 147 i2s 211
20 ldc2_w 84 bastore 148 lcmp 212
21 iload 85 castore 149 fcmpl 213
22 lload 86 sastore 150 fcmpg 214
23 fload 87 pop 151 dcmpl 215
24 dload 88 pop2 152 dcmpg 216
25 aload 89 dup 153 ifeq 217
26 iload_0 90 dup_x1 154 ifne 218
27 iload_1 91 dup_x2 155 iflt 219
28 iload_2 92 dup2 156 ifge 220
29 iload_3 93 dup2_x1 157 ifgt 221
30 lload_0 94 dup2_x2 158 ifle 222
31 lload_1 95 swap 159 if_icmpeq 223
32 lload_2 96 iadd 160 if_icmpne 224
33 lload_3 97 ladd 161 if_icmplt 225
34 fload_0 98 fadd 162 if_icmpge 226
35 fload_1 99 dadd 163 if_icmpgt 227
36 fload_2 100 isub 164 if_icmple 228
37 fload_3 101 lsub 165 if_acmpeq 229
38 dload_0 102 fsub 166 if_acmpne 230
39 dload_1 103 dsub 167 goto 231
40 dload_2 104 imul 168 jsr 232
41 dload_3 105 lmul 169 ret 233
42 aload_0 106 fmul 170 tableswitch 234
43 aload_1 107 dmul 171 lookupswitch 235
44 aload_2 108 idiv 172 ireturn 236
45 aload_3 109 ldiv 173 lreturn 237
46 iaload 110 fdiv 174 freturn 238
47 laload 111 ddiv 175 dreturn 239
48 faload 112 irem 176 areturn 240
49 daload 113 lrem 177 return 241
50 aaload 114 frem 178 getstatic 242
51 baload 115 drem 179 putstatic 243
52 caload 116 ineg 180 getfield 244
53 saload 117 lneg 181 putfield 245
54 istore 118 fneg 182 invokevirtual 246
55 lstore 119 dneg 183 invokespecial 247
56 fstore 120 ishl 184 invokestatic 248
57 dstore 121 lshl 185 invokeinterface 249
58 astore 122 ishr 186 invokedynamic 250
59 istore_0 123 lshr 187 new 251
60 istore_1 124 iushr 188 newarray 252
61 istore_2 125 lushr 189 anewarray 253
62 istore_3 126 iand 190 arraylength 254 impdep1
63 lstore_0 127 land 191 athrow 255 impdep2

参考: Chapter 6. The Java Virtual Machine Instruction Set

mnemonic symbol和类型信息

对于大多数的opcode,它的mnemonic symbol名字当中带有类型信息(type information)。一般情况下,规律如下:

  • i表示int类型
  • l表示long类型。
  • s表示short类型
  • b表示byte类型
  • c表示char类型
  • f表示float类型
  • d表示double类型。
  • a表示reference类型。a可能是address的首字母,表示指向内存空间的一个地址信息。

有一些opcode,它的mnemonic symbol名字不带有类型信息(type information),但是可以操作多种类型的数据,例如:

  • arraylength,无论是int[]类型的数组,还是String[]类型的数组,获取数组的长度都是使用arraylength。
  • ldc、ldc_w和ldc2_w表示load constant的缩写,可以加载各种常量值。
    与stack相关的指令,可以操作多种数据类型。pop表示出栈操作,dup相关的指令是duplicate单词的缩写,表示“复制”操作;swap表示“交换”。

还有一些opcode,它的mnemonic symbol名字不带有类型信息(type information),它也不处理任何类型的数据。例如:

  • goto

For the majority of typed instructions, the instruction type is represented explicitly in the opcode mnemonic by a letter: i for an int operation, l for long, s for short, b for byte, c for char, f for float, d for double, and a for reference. Some instructions for which the type is unambiguous do not have a type letter in their mnemonic. For instance, arraylength always operates on an object that is an array. Some instructions, such as goto, an unconditional control transfer, do not operate on typed operands.
参考: 2.11.1. Types and the Java Virtual Machine

小结

  • 在ClassFile结构中,对于方法接收的参数和方法体的大小是有数量限制的。
  • instruction = opcode + operands
  • opcode占用1个byte大小,目前定义了205个;为了方便记忆,JVM文档为opcode提供了名字(即mnemonic symbol),大多数的名字中带有类型信息。
  • 学习opcode是一个长期积累的过程,不能一蹴而就。所以,推荐大家依据感兴趣的内容,进行有选择性的学习。
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,142评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,298评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,068评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,081评论 1 291
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,099评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,071评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,990评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,832评论 0 273
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,274评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,488评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,649评论 1 347
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,378评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,979评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,625评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,796评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,643评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,545评论 2 352

推荐阅读更多精彩内容