Java .class文件的最大大小是多少?

时间:2017-02-17 10:05:26

标签: java jvm specifications .class-file

.class文件相当于well documented format,它定义了部分和大小,因此也定义了最大尺寸。

例如,一个.class文件包含一个幻数(4个字节),一个版本(4个字节),一个常量池(可变大小)等。但是可以在几个级别定义大小:你可以有65535个方法,每个方法限制为65535个字节。

其他限制是什么?而且,如果您可以制作最大的.class文件,它的大小是多少?

如果需要,限制Java的答案。这意味着如果Scala或Clojure(或......)改变了某些限制,则忽略这些值。

3 个答案:

答案 0 :(得分:17)

JVM规范没有规定类文件的限制,因为类文件是可扩展的容器,支持arbitrary custom attributes,你甚至可以根据需要最大化它。

u4类型的每个attribute has a size field都可以指定最多2³²-14GiB)的数字。实际上,由于JRE API(ClassLoader方法,Instrumentation API和Unsafe)始终使用byte[]ByteBuffer来描述类文件,因此无法创建具有超过2³¹-1个字节(2GiB)的类文件的运行时类。

换句话说,即使单个自定义属性的大小也可能超过实际可加载类的大小。但是一个类可以有65535个属性,加上65535个字段,每个属性都有自己的65535个属性,还有65535个方法,每个属性最多也有65535个属性。

如果你进行数学运算,你会得出结论,一个仍然很好的类文件的理论最大值可能超过任何实际存储空间(超过2个字节)。

答案 1 :(得分:12)

使用嵌套的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-file。您还可以添加更多构造函数,但javac可能会因OutOfMemoryError而失败(使用javac -J-Xmx4G来克服此问题)。

答案 2 :(得分:3)

具有方法的类的理论,半实际限制很可能受常量池的约束。所有方法只能有64K。 java.awt.Component有2863个常量和83548个字节。具有相同字节/常量比的类将耗尽1.9 MB的常量池。相比之下,像com.sun.corba.se.impl.logging.ORBUtilSystemException这样的类将耗尽大约3.1 MB。

对于大型类,您可能会在常量池中耗尽大约2 - 3 MB的常量。

通过契约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)