web-dev-qa-db-fra.com

Quelle est la taille maximale d'un fichier Java .class?

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.

23
Olivier Grégoire

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).

18
Holger

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).

13
Tagir Valeev

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)
3
Peter Lawrey