我知道,只要 Y>= X,JDK X 版本生成的字节代码可以保证在 JVM Y 上运行。
这是否适用于所有版本的JDK/JVM? 即期望 JDK 1 生成的类文件在 JVM 11 上运行是否公平?
参考 JVM 规范、JDK 8 兼容性指南和 Java 11 JSR 在那里找不到准确的答案。
字节码本身应该可以在未来的版本中工作。到目前为止,这仍然是正确的,但没有人知道这是否适用于所有未来。
更改并可能破坏程序的是 API 中的更改。弃用的 API 将来可能会消失,然后您的程序将不再工作,并且在引用此类方法时可能会引发java.lang.NoSuchMethodError
。
Java 字节码不向前兼容,JVM 向后兼容。这些属性之间的区别在于,任何未来的 JVM 都可能决定向后放弃对某个旧字节代码版本的兼容性。
Java字节代码的设计方式很少需要这种削减,但是已经故意限制了向后兼容性。从Java 8开始,对Java 1.0invokespecial
的不同语义的支持已被放弃。正如 JVM 规范 §4.1 所述:
ACC_SUPER
标志指示如果调用特殊指令 (§invokespecial) 出现在此类或接口中,则由两个替代语义中的哪一个来表示。Java 虚拟机指令集的编译器应设置ACC_SUPER
标志。在 Java SE 8 及更高版本中,Java 虚拟机会考虑在每个class
文件中设置ACC_SUPER
标志,而不考虑class
文件中标志的实际值和class
文件的版本。
ACC_SUPER
标志的存在是为了向后兼容由较旧的编译器为 Java 编程语言编译的代码。在 1.0.2 之前的 JDK 版本中,编译器生成了access_flags
其中现在表示ACC_SUPER
的标志没有分配的含义,并且 Oracle 的 Java 虚拟机实现会忽略该标志(如果设置了该标志)。
这并不意味着早期的Java 1.0代码通常不起作用。只有依赖于该早期版本的invokespecial
的过时和现在不支持的语义的代码才会中断。
另一个更改是删除了jsr
和ret
的说明¹,但是,此更改已绑定到较新的类文件版本,因此较旧的类文件版本仍支持这些说明,因此它不会破坏现有代码。但这可能是未来JVM确实放弃对这些旧版本的支持的原因。
¹ JVM 规范 §4.9.1:
如果
class
文件版本号为 51.0 或更高版本,则jsr操作码或jsr_w操作码都不能出现在code
数组中。
ret
指令尚未提及,但如果没有jsr
说明,则不起作用。