Java .class 文件的最大大小是多少?
What is the maximum size of a Java .class file?
.class
文件是一个相当 well documented format 的文件,它定义了部分和大小,因此也定义了最大大小。
例如,.class
文件包含幻数(4 字节)、版本(4 字节)、常量池(可变大小)等。但大小可以在多个级别上定义:您可以有 65535 个方法,每个方法限制为 65535 个字节。
还有哪些限制?而且,如果您要制作最大的 .class
文件,它的大小是多少?
如果需要,请将答案限制为 Java。这意味着如果 Scala 或 Clojure(或...)改变了某些限制,请忽略这些值。
理论上,semi-realistic 方法的 class 限制很可能受常量池的限制。所有方法只能有 64K。 java.awt.Component
有 2863 个常量和 83548 个字节。与 bytes/constant 具有相同比率的 class 将 运行 从常量池中取出 1.9 MB。相比之下,像 com.sun.corba.se.impl.logging.ORBUtilSystemException
这样的 class 会 运行 大约 3.1 MB。
对于较大的 class,您可能 运行 常量池中的常量约 2 - 3 MB。
根据合同 sun.awt.motif.X11GB18030_1$Encoder
充满了大常量字符串,它有 122KB,只有 68 个常量。这个class没有任何方法。
为了实验,我的编译在大约 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)
JVM 规范不强制限制 class 个文件,并且由于 class 个文件是可扩展的容器,支持 arbitrary custom attributes,您甚至可以尽可能多地使用它愿望.
u4
类型的每个 attribute has a size field,因此,最多可以指定 2³²-1
(4GiB
)的数量。因为在实践中,JRE API(ClassLoader
方法、Instrumentation API 和 Unsafe
)都始终使用 byte[]
或 ByteBuffer
来描述class 个文件,无法为超过 2³¹-1
个字节(2GiB
)的 class 文件创建运行时 class。
换句话说,即使是单个自定义属性的大小也可能超过实际可加载的 classes 的大小。但是 class 可以有 65535 个属性,外加 65535 个字段,每个字段都有 65535 个自己的属性,还有 65535 个方法,每个方法最多也有 65535 个属性。
如果你计算一下,你会得出这样的结论:一个结构良好的 class 文件的理论最大值可能超过任何实际存储空间 space(超过 2⁶⁵ 字节)。
使用嵌套的 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 文件在使用 Java 8 javac 编译时产生 105,236,439 字节。class-file。您还可以添加更多构造函数,尽管存在 javac 因 OutOfMemoryError
而失败的风险(使用 javac -J-Xmx4G
来克服此问题)。
.class
文件是一个相当 well documented format 的文件,它定义了部分和大小,因此也定义了最大大小。
例如,.class
文件包含幻数(4 字节)、版本(4 字节)、常量池(可变大小)等。但大小可以在多个级别上定义:您可以有 65535 个方法,每个方法限制为 65535 个字节。
还有哪些限制?而且,如果您要制作最大的 .class
文件,它的大小是多少?
如果需要,请将答案限制为 Java。这意味着如果 Scala 或 Clojure(或...)改变了某些限制,请忽略这些值。
理论上,semi-realistic 方法的 class 限制很可能受常量池的限制。所有方法只能有 64K。 java.awt.Component
有 2863 个常量和 83548 个字节。与 bytes/constant 具有相同比率的 class 将 运行 从常量池中取出 1.9 MB。相比之下,像 com.sun.corba.se.impl.logging.ORBUtilSystemException
这样的 class 会 运行 大约 3.1 MB。
对于较大的 class,您可能 运行 常量池中的常量约 2 - 3 MB。
根据合同 sun.awt.motif.X11GB18030_1$Encoder
充满了大常量字符串,它有 122KB,只有 68 个常量。这个class没有任何方法。
为了实验,我的编译在大约 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)
JVM 规范不强制限制 class 个文件,并且由于 class 个文件是可扩展的容器,支持 arbitrary custom attributes,您甚至可以尽可能多地使用它愿望.
u4
类型的每个 attribute has a size field,因此,最多可以指定 2³²-1
(4GiB
)的数量。因为在实践中,JRE API(ClassLoader
方法、Instrumentation API 和 Unsafe
)都始终使用 byte[]
或 ByteBuffer
来描述class 个文件,无法为超过 2³¹-1
个字节(2GiB
)的 class 文件创建运行时 class。
换句话说,即使是单个自定义属性的大小也可能超过实际可加载的 classes 的大小。但是 class 可以有 65535 个属性,外加 65535 个字段,每个字段都有 65535 个自己的属性,还有 65535 个方法,每个方法最多也有 65535 个属性。
如果你计算一下,你会得出这样的结论:一个结构良好的 class 文件的理论最大值可能超过任何实际存储空间 space(超过 2⁶⁵ 字节)。
使用嵌套的 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 文件在使用 Java 8 javac 编译时产生 105,236,439 字节。class-file。您还可以添加更多构造函数,尽管存在 javac 因 OutOfMemoryError
而失败的风险(使用 javac -J-Xmx4G
来克服此问题)。