今天当发版工具人时遇到编译报错「代码过长(code too large)」,把这段说明发到工作群里,瞬间听到周围一片爽朗的笑声……
挺让我惊讶,原来 Java 里的方法大小是有限制的——编译后的字节码大小不能超过 64kb。
JVM 规范 Chapter 4. The class File Format 在 class
结构的 method_info
(方法信息)里定义了一个 u1 类型[1]的数组来保存该 Java 方法编译后的数据,这个数组的大小由一个 u2 类型[2]的变量确定。
1 个 u2 类型的值占 2 byte, 2 byte 就是 16 bit(位),16 bit 所能表示的最大值是 2 的 16 次方——65536。因此一个 u2 类型的变量的最大值就是 65536,所以这个数组也就最多只能保存 65536 byte,即 64kb 的数据。(1 kb = 1024 byte)
这个错误虽然可以在编译期间暴露,但 JVM 并不能保证它加载的 class 文件都是满足要求的,因此在加载 class 文件的验证阶段,还会对该数组的长度进行检查。
除了普通方法外,构造方法、初始化代码块也受到此限制。
初始化代码块为什么也受到此限制呢?我猜测初始化代码块在 JVM 层面还是当作方法来使用的。
同时,如果一个 Java 类里有多个初始化代码块,虽然每个代码块里的代码没有超出此限制,但当它们之和超过该限制时,也会编译报错「代码过长」。我猜测多个初始化代码块在 JVM 层面会被优化成一个方法来使用。
代码块保存在 JVM 的哪块区域?
比如:
1 | public class DayOne{ |
更有趣的是,Java 中数组的大小理论上与 int 的取值范围相关,但实际上可能在初始化数组时,还未达到最大容量便报错「代码过长」:
1 | { |
类似的,JVM 规范 还介绍了「类或接口可以声明的字段数量限制在 65535」「方法参数的数量限制为 255」等情况。:)