Java 20解构特性疑似Bug:启用switch分支触发VerifyError
这是Java 20的Bug还是我的理解疏漏?
我写了一段使用模式匹配switch的Java代码,原本编译运行能得到预期输出,但保留switch语句中带when守卫的Point解构分支后,代码能正常编译,但在启用预览特性的MacOS 13.3.1 ARM64 JDK 20环境中运行时,抛出了java.lang.VerifyError错误。
测试代码
import static java.lang.System.*; public class Main { record Point(int x, int y, Color color){} enum Color {RED, BLUE, YELLOW} public static void main(String[] args) { testSwitch(new Point(10,20,Color.BLUE)); testSwitch(new Point(10,20,Color.RED)); testSwitch("vbjhjbhbjhbj"); testSwitch(1.012); testSwitch(Color.YELLOW); } static void testSwitch(Object o){ switch(o){ case null -> out.println("Null"); case Color c -> out.println("Color: "+c); case Point(var x, var y, Color c) when c == Color.RED -> out.printf("Red point x=%d, y=%d\n",x,y); case Point(var x, var y, var color) -> out.printf("Point x=%d, y=%d, color=%s\n",x,y,color); case String s when s.length() >=7 -> out.printf("Big string: '%s', of size %d\n",s,s.length()); case String s -> out.printf("Small string: %s, of size %d\n",s,s.length()); default -> out.println("Unexpected value: " + o); } } }
运行错误信息
/Library/Java/JavaVirtualMachines/jdk-20.jdk/Contents/Home/bin/java --enable-preview -javaagent:/Applications/IntelliJ IDEA.app/Contents/lib/idea_rt.jar=50788:/Applications/IntelliJ IDEA.app/Contents/bin -Dfile.encoding=UTF-8 -Dsun.stdout.encoding=UTF-8 -Dsun.stderr.encoding=UTF-8 -classpath /Users/hristo/projects/java20tests/java20tests/out/production/java20tests Main Error: Unable to initialize main class Main Caused by: java.lang.VerifyError: Bad local variable type Exception Details: Location: Main.testSwitch(Ljava/lang/Object;)V @309: aload Reason: Type top (current frame, locals[7]) is not assignable to reference type Current Frame: bci: @309 flags: { } locals: { 'java/lang/Object', 'java/lang/Object', integer, top, 'Main$Point', integer, integer, top, integer, integer, integer, integer, 'Main$Color', integer } stack: { 'java/io/PrintStream', 'java/lang/String', '[Ljava/lang/Object;', '[Ljava/lang/Object;', integer } Bytecode: 0000000: 2a4c 033d 2b1c ba00 2800 00aa 0000 019a 0000010: ffff ffff 0000 0003 0000 0021 0000 002c 0000020: 0000 0043 0000 013f 0000 0174 b200 2c12 0000030: 32b6 0034 a701 832b c000 0a4e b200 2c2d 0000040: b800 3aba 003f 0000 b600 34a7 016c 2bc0 0000050: 0007 3a04 1904 b600 4336 0803 3609 1508 0000060: b800 4715 09ba 004c 0000 ab00 0000 00db 0000070: 0000 0002 ffff ffff 0000 00db 0000 0000 0000080: 0000 001a 1508 3605 1904 b600 4d36 0a03 0000090: 360b 150a b800 4715 0bba 004c 0000 ab00 00000a0: 0000 00a1 0000 0002 ffff ffff 0000 00a1 00000b0: 0000 0000 0000 001a 150a 3606 1904 b600 00000c0: 503a 0c03 360d 190c 150d ba00 5400 00ab 00000d0: 0000 004b 0000 0002 ffff ffff 0000 004b 00000e0: 0000 0000 0000 0019 190c 3a07 1907 b200 00000f0: 18a5 0009 0436 0da7 ffcf b200 2c12 5805 0000100: bd00 0259 0315 05b8 0047 5359 0415 06b8 0000110: 0047 53b6 005a 57a7 00a0 b200 2c12 5e06 0000120: bd00 0259 0315 05b8 0047 5359 0415 06b8 0000130: 0047 5359 0519 0753 b600 5a57 a700 7b04 0000140: 3609 a7ff 1c05 3da7 febd 2bc0 003b 3a08 0000150: 1908 b600 6010 07a2 0008 063d a7fe a8b2 0000160: 002c 1263 05bd 0002 5903 1908 5359 0419 0000170: 08b6 0060 b800 4753 b600 5a57 a700 3b2b 0000180: c000 3b3a 09b2 002c 1265 05bd 0002 5903 0000190: 1909 5359 0419 09b6 0060 b800 4753 b600 00001a0: 5a57 a700 15b2 002c 2ab8 003a ba00 6700 00001b0: 00b6 0034 a700 03a7 0011 4cbb 006a 592b 00001c0: b600 6c2b b700 70bf b1 Exception Handler Table: bci [86, 89] => handler: 442 bci [138, 141] => handler: 442 bci [190, 193] => handler: 442 Stackmap Table: append_frame(@4,Object[#2],Integer) same_frame(@44) same_frame(@55) same_frame(@78) full_frame(@94,{Object[#2],Object[#2],Integer,Top,Object[#7],Top,Top,Top,Integer,Integer},{}) same_frame(@132) full_frame(@146,{Object[#2],Object[#2],Integer,Top,Object[#7],Integer,Top,Top,Integer,Integer,Integer,Integer},{}) same_frame(@184) full_frame(@198,{Object[#2],Object[#2],Integer,Top,Object[#7],Integer,Integer,Top,Integer,Integer,Integer,Integer,Object[#10],Integer},{}) same_frame(@232) full_frame(@250,{Object[#2],Object[#2],Integer,Top,Object[#7],Integer,Integer,Object[#10],Integer,Integer,Integer,Integer,Object[#10],Integer},{}) full_frame(@282,{Object[#2],Object[#2],Integer,Top,Object[#7],Integer,Integer,Top,Integer,Integer,Integer,Integer,Object[#10],Integer},{}) full_frame(@319,{Object[#2],Object[#2],Integer,Top,Object[#7],Integer,Top,Top,Integer,Integer,Integer,Integer},{}) full_frame(@325,{Object[#2],Object[#2],Integer,Top,Object[#7],Top,Top,Top,Integer,Integer},{}) full_frame(@330,{Object[#2],Object[#2],Integer},{}) full_frame(@351,{Object[#2],Object[#2],Integer,Top,Top,Top,Top,Top,Object[#59]},{}) full_frame(@383,{Object[#2],Object[#2],Integer},{}) same_frame(@421) chop_frame(@439,2) same_locals_1_stack_item_frame(@442,Object[#104]) same_frame(@456) Process finished with exit code 1
结论与解决建议
这是Java 20的Bug,和你的代码逻辑、理解无关。具体是JDK编译器在处理带when守卫的record解构模式匹配switch分支时,生成的字节码不符合JVM的验证规则,导致运行时触发VerifyError。这类问题属于预览特性的实现缺陷,因为模式匹配switch在Java 20中还处于预览阶段,正式版的Java 21已经修复了大量此类问题。
解决建议:
- 直接升级到Java 21,正式版的模式匹配switch已经稳定,不会出现这类验证错误
- 如果必须使用Java 20,可以尝试调整分支顺序(比如把带when的Point分支放到普通Point分支之后),或者换一种写法避开解构+when的组合
- 等待Oracle发布JDK 20的补丁修复该问题
内容的提问来源于stack exchange,提问作者Hristo Stoyanov
相关产品推荐
相关产品推荐

