que - java doc return
¿Cuál es el tamaño máximo de un archivo.class de Java? (3)
Un archivo .class
es un formato bastante bien documentado que define las secciones y el tamaño, y por lo tanto los tamaños máximos también.
Por ejemplo, un archivo .class
contiene un número mágico (4 bytes), una versión (4 bytes), el conjunto constante (tamaño variable), etc. Pero los tamaños se pueden definir en varios niveles: puede tener 65535 métodos y cada uno es Limitado a 65535 bytes.
¿Cuáles son los otros límites? Y, si .class
posible el mayor archivo .class
, ¿de qué tamaño sería?
Si es necesario, limite las respuestas a Java. Lo que significa que si Scala o Clojure (o ...) cambian algunos límites, ignore esos valores.
El límite teórico, semi-realista para una clase con métodos es muy probablemente limitado por el conjunto constante. Solo puedes tener 64K en todos los métodos. java.awt.Component
tiene 2863 constantes y 83548 bytes. Una clase que tuviera la misma proporción de bytes / constante se quedaría sin grupo constante a 1.9 MB. En comparación, una clase como com.sun.corba.se.impl.logging.ORBUtilSystemException
se com.sun.corba.se.impl.logging.ORBUtilSystemException
alrededor de 3.1 MB.
Para una clase grande, es probable que se quede sin constante en el conjunto constante de 2 a 3 MB.
Por contrato sun.awt.motif.X11GB18030_1$Encoder
está lleno de cadenas constantes grandes y tiene 122KB con solo 68 constantes. Esta clase no tiene ningún método.
Para la experimentación, mi compilación explota con demasiadas constantes en alrededor de 21800 constantes.
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("}");
}
}
También parece que el compilado carga el texto en un ByteBuffer. Esto significa que la fuente no puede ser de 1 GB o que el compilador recibe este error. Mi conjetura es que los caracteres en bytes se han desbordado a un número negativo.
java.lang.IllegalArgumentException
at java.nio.ByteBuffer.allocate(ByteBuffer.java:334)
at com.sun.tools.javac.util.BaseFileManager$ByteBufferCache.get(BaseFileManager.java:325)
Es bastante fácil hacer un gran StackMapTable usando bloques anidados finalmente, ya que javac genera imprudentemente variables separadas para cada nivel de anidamiento. Esto permite producir varios megabytes a partir de un método muy simple como este:
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;
}}}}}}}}}}}}
}}
No es posible agregar más niveles de anidamiento ya que excederá el tamaño del código para un solo método. También puede duplicar esto usando el hecho de que el inicializador de instancia se copia a cada 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) { }
}
Este simple archivo java cuando se compila con Java 8 javac produce 105,236,439 bytes .class-file. También puede agregar más constructores, aunque existe el riesgo de que javac falle con OutOfMemoryError
(use javac -J-Xmx4G
para superar esto).
La especificación de JVM no impone un límite para los archivos de clase y como los archivos de clase son contenedores extensibles, que admiten atributos personalizados arbitrarios , puede incluso maximizarlos tanto como desee.
Cada atributo tiene un campo de tamaño del tipo u4
, por lo tanto, podría especificar un número de hasta 2³²-1
( 4GiB
). Dado que, en la práctica, la API de JRE (métodos de ClassLoader
, API de instrumentación e Unsafe
) utilizan de manera consistente ya sea byte[]
o ByteBuffer
para describir los archivos de clase, es imposible crear una clase de tiempo de ejecución de un archivo de clase que tenga más de 2³¹-1
bytes ( 2GiB
).
En otras palabras, incluso un único atributo personalizado podría tener un tamaño que exceda el tamaño de las clases realmente cargables. Pero una clase puede tener 65535 atributos, más 65535 campos, cada uno de ellos con 65535 atributos propios y más 65535 métodos, cada uno de ellos con hasta 65535 atributos también.
Si realiza los cálculos matemáticos, llegará a la conclusión de que el máximo teórico de un archivo de clase aún bien formado puede exceder cualquier espacio de almacenamiento real (más de 2⁶⁵ bytes).