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 Specification的 4.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 Specification的 4.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是一个长期积累的过程,不能一蹴而就。所以,推荐大家依据感兴趣的内容,进行有选择性的学习。
网友评论