一个.class
文件是一种相当良好记录的格式,它定义了各个部分和大小,因此也定义了最大大小。
例如,.class
文件包含一个魔数(4个字节),一个版本(4个字节),常量池(可变大小)等。但是可以在多个级别上定义大小:您可以拥有65535个方法,每个方法限制为65535个字节。
其他限制是什么?如果要创建最大的.class
文件,它会有多大?
如有必要,请将答案限制在Java中。这意味着如果Scala或Clojure(或...)更改了某些限制,请忽略这些值。
一个.class
文件是一种相当良好记录的格式,它定义了各个部分和大小,因此也定义了最大大小。
例如,.class
文件包含一个魔数(4个字节),一个版本(4个字节),常量池(可变大小)等。但是可以在多个级别上定义大小:您可以拥有65535个方法,每个方法限制为65535个字节。
其他限制是什么?如果要创建最大的.class
文件,它会有多大?
如有必要,请将答案限制在Java中。这意味着如果Scala或Clojure(或...)更改了某些限制,请忽略这些值。
JVM规范没有规定类文件的限制,因为类文件是可扩展的容器,支持任意自定义属性,所以您甚至可以将其最大化。
每个属性都有一个大小字段,类型为u4
,因此可以指定高达2³²-1
(4GiB
)的数字。由于在实践中,JRE API(ClassLoader
方法、Instrumentation API和Unsafe
)都一致使用byte[]
或ByteBuffer
来描述类文件,因此不可能创建具有超过2³¹-1
字节(2GiB
)的类文件的运行时类。
.class
格式中已经考虑到了所有的东西。我的错。这基本上是任何人可以给出的最终答案了。接下来几天我会将你的回答标记为被接受的答案,以便其他有见地的答案也能够出现。 - Olivier Grégoirebyte[]
也不使用ByteBuffer
或Unsafe
。但是,HotSpot classFileParser
也依赖于int
变量来处理类文件流。 - apanginURLClassLoader
,因此与众所周知的API绑定。如果引导加载器实现受到同样类型的限制也不会让我感到惊讶,但在实际情况下,引导加载器读取共享类数据(.jsa)存档而不是类文件。 - Holger使用嵌套的finally块可以轻松地制作巨大的StackMapTable,因为javac不明智地为每个嵌套级别生成单独的变量。这可以使非常简单的方法产生几兆字节的数据:
class A {{
int a;
try {a=0;} finally {
try {a=0;} finally {
try {a=0;} finally {
try {a=0;} finally {
try {a=0;} finally {
try {a=0;} finally {
try {a=0;} finally {
try {a=0;} finally {
try {a=0;} finally {
try {a=0;} finally {
try {a=0;} finally {
try {a=0;} finally {
a=0;
}}}}}}}}}}}}
}}
由于单个方法的代码大小限制,无法添加更多嵌套层级。您也可以使用实例初始化程序复制到每个构造函数来进行复制:
class A {{
int a;
try {a=0;} finally {
try {a=0;} finally {
try {a=0;} finally {
try {a=0;} finally {
try {a=0;} finally {
try {a=0;} finally {
try {a=0;} finally {
try {a=0;} finally {
try {a=0;} finally {
try {a=0;} finally {
try {a=0;} finally {
try {a=0;} finally {
a=0;
}}}}}}}}}}}}
}
A() { }
A(int a) { }
A(char a) { }
A(double a) { }
A(float a) { }
A(long a) { }
A(short a) { }
A(boolean a) { }
A(String a) { }
A(Integer a) { }
A(Float a) { }
A(Short a) { }
A(Long a) { }
A(Double a) { }
A(Boolean a) { }
A(Character a) { }
}
编译使用Java 8 javac的这个简单Java文件将产生105,236,439字节的.class文件。您也可以添加更多构造函数,但有风险导致javac出现OutOfMemoryError
(使用javac -J-Xmx4G
来解决此问题)。
一个带有方法的类的理论上的、半现实的极限可能是由常量池所限制的。所有方法中的常量总数不能超过64K。java.awt.Component
类有2863个常量和83548字节,具有相同字节/常量比率的类在1.9MB时就会耗尽常量池。相比之下,像com.sun.corba.se.impl.logging.ORBUtilSystemException
这样的类则会在大约3.1MB处用尽常量池。
对于一个大型类,你很可能在常量池中用尽常量达到2-3MB左右。
相比之下,sun.awt.motif.X11GB18030_1$Encoder
中充满了大量的常量字符串,这个类只有68个常量,但大小为122KB。该类没有任何方法。
经过实验,我的编译在21800个常量左右就会爆炸。
public static void main(String[] args) throws FileNotFoundException {
try (PrintWriter out = new PrintWriter("src/main/java/Constants.java")) {
out.println("class Constants {");
for (int i = 0; i < 21800; i++) {
StringBuilder sb = new StringBuilder();
while (sb.length() < 100)
sb.append(i).append(" ");
out.println("private static final String c" + i + " = \"" + sb + "\";");
}
out.println("}");
}
}
还有一个问题,似乎编译器会将文本加载到ByteBuffer中。这意味着源代码不能大于1 GB,否则编译器将出现错误。我猜测字节中的字符已经溢出成了负数。
java.lang.IllegalArgumentException
at java.nio.ByteBuffer.allocate(ByteBuffer.java:334)
at com.sun.tools.javac.util.BaseFileManager$ByteBufferCache.get(BaseFileManager.java:325)
.class
文件,而不是编译成.class
文件的.java
文件。 - Olivier Grégoire.java
文件。如果使用三个字节的字符,则可能意味着约3 GB。 - Peter LawreyByteBuffer
,试图分配一些字节。从这个地方,我无法推断出与char
的关系。但问题本来就是关于类文件而不是源文件的... - Holger
.class
文件的最大大小。 - Olivier Grégoire