Java中的“代码过大”编译错误

112

Java中的代码有最大长度限制吗?我写了一个超过10,000行的函数。实际上,每一行都将一个值赋给一个数组变量。

arts_bag[10792]="newyorkartworld";
arts_bag[10793]="leningradschool";
arts_bag[10794]="mailart";
arts_bag[10795]="artspan";
arts_bag[10796]="watercolor";
arts_bag[10797]="sculptures";
arts_bag[10798]="stonesculpture"; 

编译时出现错误:代码太大。

我该如何解决?


10
我只是惊呆了……一定有更好的方法来做这件事。 - Thanos Papathanasiou
3
是的,我同意 - 设计是没有希望的。最初,数组的大小只有64,所以我觉得没有必要从文件中读取...现在代码的其余部分依赖于这个数组,所以我想我可以找到一个临时的解决方案,然后稍后通过从文件中读取来重新安排事情。好的,我会尝试你的建议,谢谢! - trinity
9
对于这种情况,您确实需要查看数据库,如果不行的话,可以考虑属性文件。 - Paul Whelan
1
三位一体。你最初说这只限于64,所以这没问题。在编程时应始终考虑意外扩展,第一次就做对将节省未来的时间,而且说实话,具有查找方法的.properties文件并不需要额外的工作量。始终要认真思考你正在编写的任何代码是否可能在未来扩展,并相应地编写代码。放置“临时”解决方案从来都不会有好结果,它们最终会进入生产代码中,并被遗忘,直到你遇到问题,或者其他人必须进行大规模重构以解决它。 - David Billings
9
为什么要让你的应用程序在启动时花费大量时间解析文本文件,而不是在编译时让编译器预先生成所有内容呢?如果您想要在不重新编译的情况下更改数据或手动编写方法,则这是糟糕的设计,但如果您生成源代码,则这并不是糟糕的设计。(至少如果您以一种实际允许编译器预先生成数组的方式进行)。 - Guntram Blohm
显示剩余4条评论
14个回答

112

Java类中的单个方法所生成的字节码不能超过64KB。

但您应该对此进行清理!

使用 .properties 文件来存储这些数据,并通过 java.util.Properties 加载它。

您可以将 .properties 文件放置在类路径上,并使用以下代码:

Properties properties = new Properties();
InputStream inputStream = getClass().getResourceAsStream("yourfile.properties");
properties.load(inputStream);

2
“无论您的JDK/JVM实际大小是多少”?您是否意味着这个限制不是固定的?因为它是由类文件格式规范所要求的,是固定的。 - Joachim Sauer
1
我在哪里可以找到.properties文件? - trinity
1
你可以创建自己的类并将其放置在类路径上。 - Mark
3
我虽然很想尝试你的建议,但我没有使用它。现在我已经使用数据库来存储这些信息,并相应地修改了代码。 - trinity
1
很抱歉如果这是一个愚蠢的问题,但是.. 这个 .properties 文件应该放在哪里?我的意思是,它在类路径中,但是它在哪里? - Milack27
显示剩余7条评论

22

对于方法,存在一个64K字节代码大小限制。

话虽如此,我必须同意Richard的观点;为什么你需要一个如此庞大的方法呢?根据原帖中的示例,一个属性文件应该足够...或者如果需要的话,甚至可以使用数据库。


6
枚举类型怎么样?我在使用大量枚举类型时遇到了相同的问题。 - Toby
1
@Toby:我自己从未遇到过枚举的这个问题。不过,这里有其他用户在SO上发布的关于同样问题的帖子。例如-https://dev59.com/m3E85IYBdhLWcg3w64EA 也许值得查看生成的.class文件以查看enum - Everyone
枚举实例(即代表常量的对象)是在类的静态初始化程序中创建的,该程序是一个方法,并具有相同的限制。 - juancn
我在处理一个非常复杂的Web表单时,也遇到了由框架生成的代码问题。解决方案是将表单拆分成组件,这样每个组件的生成代码也会被分开。 - SebaGra

18
根据Java虚拟机规范一个方法的代码不能超过65536个字节

code_length的值给出该方法code数组中的字节数。

code_length的值必须大于零(因为代码数组不能为空),且小于65536。

code_length定义了code[]属性的大小,该属性包含一个方法的实际字节码:

code数组给出实现该方法的Java虚拟机代码的实际字节。


1
这不仅仅与方法大小有关... 如果你将数组设为静态类成员,它也会失败。 - ACV
1
@ACV:这是因为数组成员实际上是通过放置在静态初始化器块中的合成代码逐个初始化的,该块最终会进入代码块并像方法一样进行处理。 - Joachim Sauer
那真是很有见地。谢谢。我之前并不知道,但是看了一下字节码后,也不是很明显。那么,我猜在javap中标记为“Code:”的任何块都是这些方法之一? - ACV
1
@AVC:Code: 部分只是各种方法和初始化器的字节码(例如静态初始化器标记为 static {};)。因此,它包含了方法的“正常”代码和编译器生成的合成代码,没有明显的区别。 - Joachim Sauer

6

这似乎有点疯狂。你不能通过读取文本文件或其他数据源来初始化数组吗?


7
被投票踩是因为至少需要一个理由解释为什么这种技术不好。要找到一个好的理由并不容易。 - Evgeni Sergeev

5

这个错误有时会由于单个函数中的代码太大而出现... 为了解决这个错误,将该函数拆分成多个函数,例如

//Too large code function
private void mySingleFunction(){
.
.
2000 lines of code
}
//To solve the problem
private void mySingleFunction_1(){
.
.
500 lines of code
}
private void mySingleFunction_2(){
.
.
500 lines of code
}
private void mySingleFunction_3(){
.
.
500 lines of code
}
private void mySingleFunction_4(){
.
.
500 lines of code
}
private void MySingleFunction(){
mySingleFunction_1();
mySingleFunction_2();
mySingleFunction_3();
mySingleFunction_4();
}

2

我来到这个问题是因为我正在尝试解决一个类似的问题。出于性能原因,我想将具有1600个元素的图形硬编码到2D整数数组中。我正在解决一个类似于Leetcode网站的问题,并且从文件加载图形数据不是一个选项。整个图形超过了64K的最大限制,因此我不能进行单个静态运行的分配。我将分配跨越几个静态方法,每个方法都在限制以下,然后逐个调用每个方法。

private static int[][] G = new int[1601][];

static {
    assignFirst250();
    assignSecond250();
    assignSecond500();
    assignThird500();
}

private static void assignFirst250() {
    G[1] = new int[]{3,8,15,24,35,48,63,80,99,120,143,168,195,224,255,288,323,360,399,440,483,528,575,624,675,728,783,840,899,960,1023,1088,1155,1224,1295,1368,1443,1520,1599};
    G[2] = new int[]{2,7,14,23,34,47,62,79,98,119,142,167,194,223,254,287,322,359,398,439,482,527,574,623,674,727,782,839,898,959,1022,1087,1154,1223,1294,1367,1442,1519,1598};

2

正如其他答案所述,对于一个方法来说,它的字节码限制是64KB(至少在Sun的Java编译器中是如此)。

对我而言,更有意义的做法是将该方法分解成更多的方法 - 每个方法都将相关的内容分配给数组(使用ArrayList可能更合理)。

例如:

public void addArrayItems()
{
  addSculptureItems(list);
  ...
}

public void addSculptureItems(ArrayList list)
{
  list.add("sculptures");
  list.add("stonesculpture");
}

如果这些项目是固定的,例如来自属性文件,您可以从静态资源加载这些项目。


正确的做法是将数据视为数据,将代码视为代码。 - Malcolm
@Malcolm 嗯,这显然是数据,而不是代码。你的评论有误导性,因为你不应该做的是将数据和代码混合在一起,但在这里它们并没有混合。 - Evgeni Sergeev
1
我认为这是一个不错的解决方法。我遇到了大约21k行代码和7000个路由消息的问题。我通过将这些路由消息分成7个函数并命名为xxx0、xxx1、xxx2等来解决它。 - bronze man

2

尝试重构你的代码。在Java中,方法的大小是有限制的。


1
如果该方法仅涉及数组初始化,重构似乎不是一个合理的想法。 - Gabriel Ščerbák
2
他说他的代码的其余部分依赖于这个是一个数组。因此,他可以重构该方法,将加载数据的责任传递给其他方法/工厂从文件/数据库中加载。 - Padmarag
你可以创建和初始化大型数组,而不必诉诸这种无聊的方法;请参见@Kris的答案。 - Stephen C
重构 - “在不改变计算机程序外部功能行为的情况下更改其源代码,以改善软件的某些非功能属性。”其他答案与此有何不同? - Padmarag

2

我自己也遇到了这个问题。 对我有效的解决方案是重构并缩小该方法以使其更易管理。 像你一样,我正在处理近10K行的方法。 但是,通过使用静态变量以及较小的模块化函数,问题得到了解决。

似乎会有更好的解决方法,但在使用Java 8时,没有...


1
我有一个枚举类型,导致.java文件大小超过500KB。Eclipse可以构建它,但是通过Eclipse导出的ant build.xml不能构建。我正在调查并将更新此帖子。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接