Java中的ClassFormatError

Java中的ClassFormatError,java,Java,我正在尝试帮助一个出现ClassFormatError的客户端。错误消息说 bytecode array size > 65535 at offset=66370 堆栈跟踪指示在尝试调用实用程序类中的静态方法时发生错误。我无法在自己的系统上重现错误 我尝试用谷歌搜索这个问题(search=classformaterror“bytecodearray size”,包括引号),并找到了其他软件的bug报告,但没有任何帮助。我也尝试在这里搜索,但找不到涉及此特定错误消息的结果。(也许我只是没

我正在尝试帮助一个出现ClassFormatError的客户端。错误消息说

bytecode array size > 65535 at offset=66370 
堆栈跟踪指示在尝试调用实用程序类中的静态方法时发生错误。我无法在自己的系统上重现错误

我尝试用谷歌搜索这个问题(search=classformaterror“bytecodearray size”,包括引号),并找到了其他软件的bug报告,但没有任何帮助。我也尝试在这里搜索,但找不到涉及此特定错误消息的结果。(也许我只是没能找到他们…)

知道是什么导致了这个问题吗?我不能发布代码或实际的堆栈跟踪,因为它是专有的


提前感谢您为我提供的关于查找位置的任何提示。

这表明实用程序类方法基本上太大了,需要拆分。不同的编译器将输出不同数量的代码——我猜不同的VM也可能或多或少地严格执行约束


此外,您可能会发现,在没有调试信息的情况下编译实用程序类将使该方法不受限制,但最好将其拆分。

要进一步阐述Jon Skeet的回答:

类文件格式为每个方法的字节码数组指定一个4字节长的字段,但实际上某些构造(局部变量表、行号表和异常表)将其限制为65535

前两个选项是可选的,如果编译类时没有调试信息,则不会在中编译,但第三个选项是必需的。当然,如果在方法的后半部分没有异常处理,编译器可能不会注意到它。甚至更糟糕的是,它可能溢出并编译损坏的try-catch块

无论如何,实用程序类中的一个或多个方法太大,即使没有硬编码长度限制,也可能应该拆分


更新:当然,在代码验证器的描述中,代码长度明确限制为65536字节。但不可避免地会有一些实现将其更多地视为一种指导方针

我想分享以下来自Java VM规范-Java SE 7版(准确地说是第4.7.3段)的内容:

end_pc是排他性的这一事实在过去是一个历史错误 Java虚拟机的设计:如果Java虚拟机代码 因为一个方法的长度正好是65535字节,并且以一条指令结束 即1字节长,则该指令不能由 异常处理程序。编译器编写器可以通过 限制生成的Java虚拟机代码的最大大小 对于任何方法、实例初始化方法或静态初始值设定项 (任何代码数组的大小)到65534字节


所以这似乎是编译器特有的特性。必须指出的是,在Java中生成如此庞大的代码块不应该是一个问题(如果设计合理),但当使用自动生成的代码时,它可能是一个问题。

此外,如果它只是一个充满不相关的“实用”方法的大类,也许是时候重新审视一下系统的架构了。@Radu超出的大小限制是单个方法的大小,因此很可能情况正好相反:一个长的进程被塞进了单个方法中。实用类方法大约有30条短线长,尽管其中有17行是case语句,因为整个过程是一个大的switch语句。(我没有编写代码,我只需要维护它。)这个类确实有更长的方法(和switch语句中的许多情况一样),但这个方法非常短。@smacdav:很可能是加载类本身是个问题,不管调用哪个方法。您可以使用
javap
查看字节码,看看这些方法有多大。@smacdav这有点遥不可及,但它可能是您的实用程序类引用的另一个类吗?其中有几个方法由数百个case语句组成,所有语句都通过一个return语句,但这种方式已经有很长一段时间了(在我来到这里之前很久),对于这个长期客户来说,这并不是一个问题。现在这突然成了一个问题。我会检查版本控制,看看我们最近的版本有什么变化。谢谢你的提示。@smackdav哇,同情。我还在努力。客户端使用的是我无法在系统上安装的专有JRE(安装时会进行BIOS检查,以查看您是否有合适的系统品牌来使用它)。有可能是JIT编译器造成的?JLS 12.2.1声明只有在字节格式错误时才会抛出
ClassFormatError
,因此长度限制基本上不是违反了这一标准吗?@Pacerier可能,这取决于您如何解释错误和情况。但我想这并不是你每天都会遇到的错误,而且消息文本相当可读。在我看来,这已经足够好了。