UNE .class
le fichier est plutôt format bien documenté qui définit les sections et la taille, et donc aussi les tailles maximales.
Par exemple, un .class
le fichier contient un nombre magique (4 octets), une version (4 octets), le pool constant (taille variable), etc. Mais les tailles peuvent être définies à plusieurs niveaux: vous pouvez avoir 65535 méthodes et chacune est limitée à 65535 octets .
Quelles sont les autres limites? Et, si vous voulez faire le plus grand .class
fichier possible, quelle serait sa taille?
Si nécessaire, limitez les réponses à Java. Cela signifie que si Scala ou Clojure (ou ...) change certaines limites, ignorez ces valeurs.
La spécification JVM n'impose pas de limite pour les fichiers de classe et comme les fichiers de classe sont des conteneurs extensibles, prenant en charge attributs personnalisés arbitraires , vous pouvez même le maximiser autant que vous le souhaitez.
Chaque l'attribut a un champ de taille du u4
type pourrait donc spécifier un nombre allant jusqu'à 2³²-1
(4GiB
). Étant donné que, dans la pratique, l'API JRE (méthodes ClassLoader
, API d'instrumentation et Unsafe
) utilisent systématiquement soit byte[]
ou ByteBuffer
pour décrire les fichiers de classe, il est impossible de créer une classe d'exécution d'un fichier de classe ayant plus de 2³¹-1
octets (2GiB
).
En d'autres termes, même un seul attribut personnalisé peut avoir une taille qui dépasse la taille des classes réellement chargeables. Mais une classe peut avoir 65535 attributs, plus 65535 champs, chacun d'eux ayant 65535 attributs propres et plus 65535 méthodes, chacun ayant jusqu'à 65535 attributs également.
Si vous faites le calcul, vous arriverez à la conclusion que le maximum théorique d'un fichier de classe encore bien formé peut dépasser tout espace de stockage réel (plus de 2⁶⁵ octets).
Il est assez facile de créer un énorme StackMapTable en utilisant des blocs imbriqués finalement car javac génère imprudemment des variables distinctes pour chaque niveau d'imbrication. Cela permet de produire plusieurs mégaoctets à partir d'une méthode très simple comme celle-ci:
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;
}}}}}}}}}}}}
}}
L'ajout d'un niveau d'imbrication supplémentaire n'est pas possible car vous dépasserez la taille du code pour une méthode unique. Vous pouvez également dupliquer cela en utilisant le fait que l'initialiseur d'instance est copié dans chaque constructeur:
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) { }
}
Ce simple fichier Java lorsqu'il est compilé avec Java 8 javac produit 105 236 439 octets .class-file. Vous pouvez également ajouter plus de constructeurs, bien qu'il existe un risque que javac échec avec OutOfMemoryError
(utilisez javac -J-Xmx4G
pour surmonter cela).
La limite théorique et semi-réaliste pour une classe avec des méthodes est très probablement liée par le pool constant. Vous ne pouvez avoir que 64 Ko pour toutes les méthodes. Java.awt.Component
a 2863 constantes et 83548 octets. Une classe qui avait le même rapport octets/constante manquerait de pool constant à 1,9 Mo. En comparaison, une classe comme com.Sun.corba.se.impl.logging.ORBUtilSystemException
manquerait environ 3,1 Mo.
Pour une grande classe, vous risquez de manquer de constante dans le pool constant autour de 2 à 3 Mo.
Par contrat Sun.awt.motif.X11GB18030_1$Encoder
est plein de grandes chaînes constantes et il fait 122 Ko avec seulement 68 constantes. Cette classe n'a aucune méthode.
Pour l'expérimentation, ma compilation explose avec trop de constantes à environ 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("}");
}
}
Il apparaît également que le compilé charge le texte dans un ByteBuffer. Cela signifie que la source ne peut pas être de 1 Go ou que le compilateur obtient cette erreur. Je suppose que les caractères en octets ont dépassé un nombre négatif.
Java.lang.IllegalArgumentException
at Java.nio.ByteBuffer.allocate(ByteBuffer.Java:334)
at com.Sun.tools.javac.util.BaseFileManager$ByteBufferCache.get(BaseFileManager.Java:325)