A .class
file is a rather well documented format that defines sections and size, and therefore maximum sizes as well.
For instance, a .class
file contains a magic number (4 bytes), a version (4 bytes), the constant pool (variable size), etc. But sizes can be defined on several levels: you can have 65535 methods and each is limited to 65535 bytes.
What are the other limits? And, if you would make the largest .class
file possible, what size would it be?
If needed, limit answers to Java. Meaning that if Scala or Clojure (or...) change some limits, disregard those values.
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)
来源:https://stackoverflow.com/questions/42294998/what-is-the-maximum-size-of-a-java-class-file