What is the maximum size of a Java .class file?

不羁的心 提交于 2019-11-30 00:08:08

The JVM specification doesn’t mandate a limit for class files and since class files are extensible containers, supporting arbitrary custom attributes, you can even max it out as much as you wish.

Each attribute has a size field of the u4 type, thus, could specify a number of up to 2³²-1 (4GiB). Since, in practice, the JRE API (ClassLoader methods, Instrumentation API and Unsafe) all consistently use either byte[] or ByteBuffer to describe class files, it is impossible to create a runtime class of a class file having more than 2³¹-1 bytes (2GiB).

In other words, even a single custom attribute could have a size that exceeds the size of actually loadable classes. But a class can have 65535 attributes, plus 65535 fields, each of them having 65535 attributes of its own and plus 65535 methods, each of them having up to 65535 attribute as well.

If you do the math, you will come to the conclusion that the theoretical maximum of a still well formed class file may exceed any real storage space (more than 2⁶⁵ bytes).

It's quite easy to make huge StackMapTable using nested finally blocks as javac unwisely generates separate variables for each nesting level. This allows to produce several megabytes from very simple method like this:

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;
  }}}}}}}}}}}}
}}

Adding more nesting level is not possible as you will exceed code size for single method. You can also duplicate this using the fact that instance initializer is copied to every constructor:

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) { }

}

This simple java file when compiled with Java 8 javac produces 105,236,439 bytes .class-file. You can also add more constructors, though there's a risk that javac will fail with OutOfMemoryError (use javac -J-Xmx4G to overcome this).

The theoretical, semi-realistic limit for a class with methods is most likely bound by the constant pool. You can only have 64K across all methods. java.awt.Component has 2863 constants and 83548 bytes. A class which had the same ratio of bytes/constant would run out of constant pool at 1.9 MB. By comparison a class like com.sun.corba.se.impl.logging.ORBUtilSystemException would run out around 3.1 MB.

For a large class, you are likely to run out of constant in the constant pool around 2 - 3 MB.

By contract sun.awt.motif.X11GB18030_1$Encoder is full of large constant Strings and it is 122KB with just 68 constants. This class doesn't have any methods.

For experimentation, my compile blows up with too many constants at around 21800 constants.

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("}");
    }
}

Also it appears that the compiled loads the text into a ByteBuffer. This means the source can't be 1 GB or the compiler gets this error. My guess is that the chars in bytes has overflown to a negative number.

java.lang.IllegalArgumentException
    at java.nio.ByteBuffer.allocate(ByteBuffer.java:334)
    at com.sun.tools.javac.util.BaseFileManager$ByteBufferCache.get(BaseFileManager.java:325)
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!