Größe der Initialisierungszeichenfolge in Java

Offensichtlich gibt es eine Grenze für die Größe einer Initialisierungszeichenfolge in javac. Kann mir jemand helfen zu identifizieren, was die maximale Grenze ist bitte?

Vielen Dank

bearbeiten:

Wir erstellen einen Initialisierungsstring, der ungefähr so ​​aussieht wie “{1,2,3,4,5,6,7,8 ……}”, aber idealerweise mit 10.000 Zahlen. Wenn wir dies für eine 1000 tun, funktioniert es, 10.000 triggers einen Fehler aus, der besagt, dass der Code für die try-statement zu groß ist.

Um dies zu erzeugen, verwenden wir einen Zeichenfolgengenerator und führen ein Schleifen über ein Array, das die Werte anfügt. Anscheinend ist es eine Einschränkung in Java. Uns wurde gesagt, dass wir das Array in der Methode, die wir aufrufen, neu erstellen könnten, wenn wir es in kleinen Blöcken übergeben. Dies ist jedoch nicht möglich, da wir keine Kontrolle über die Benutzermethode haben, die wir aufrufen.

Ich möchte Code schreiben, kann das aber nicht, weil es ein Projekt für die Universität ist. Ich suche keine Code-Lösungen, nur um zu verstehen, was das eigentliche Problem ist.

Es ist die for-Schleife, die der Täter ist

Object o = new Object() { public String toString() { StringBuilder s = new StringBuilder(); int length = MainInterfaceProcessor.this.valuesFromData.length; Object[] arrayToProcess = MainInterfaceProcessor.this.valuesFromData; if(length == 0) { //throw exception to do } else if(length == 1) { s.append("{" + Integer.toString((Integer)arrayToProcess[0])+"}"); } else { s.append("{" + Integer.toString((Integer)arrayToProcess[0])+","); //opening statement for(int i = 1; i < length; i++) { if(i == (length - 1)) { //last element in the array so dont add comma at the end s.append(getArrayItemAsString(arrayToProcess, i)+"}"); break; } //append each array value at position i, followed //by a comma to seperate the values s.append(getArrayItemAsString(arrayToProcess, i)+ ","); } } return s.toString(); } }; try { Object result = method.invoke(obj, new Object[] { o }); 

}

Die Länge eines String-Literals (dh "..." ) wird durch die CONSTANT_Utf8_info Struktur des classndateiformats begrenzt, auf die sich die CONSTANT_String_info Struktur bezieht.

 CONSTANT_Utf8_info { u1 tag; u2 length; u1 bytes[length]; } 

Limitierender Faktor ist hier das Längenattribut, das nur 2 Byte groß ist, dh einen Maximalwert von 65535 hat. Diese Zahl entspricht der Anzahl der Bytes in einer modifizierten UTF-8-Darstellung des Strings (das ist eigentlich fast CESU-8 , aber das Zeichen 0 wird auch in einer Zwei-Byte-Form dargestellt).

So kann ein reines ASCII-String-Literal bis zu 65535 Zeichen haben, während ein String, der aus Zeichen im Bereich U + 0800 … U + FFFF besteht, nur ein Drittel davon hat. Und diejenigen, die als Ersatzpaare in UTF-8 (dh U + 10000 bis U + 10FFFF) codiert sind, benötigen jeweils 6 Bytes.

(Dasselbe Limit gibt es für Bezeichner, dh classn-, Methoden- und Variablennamen und Typdeskriptoren für diese, da sie die gleiche Struktur verwenden.)

Die Java-Sprachspezifikation erwähnt keine Beschränkung für Zeichenfolgenliterale :

Ein Zeichenfolgenliteral besteht aus null oder mehr Zeichen in Anführungszeichen.

Im Prinzip könnte ein Compiler also ein längeres String-Literal in mehr als eine CONSTANT_String_info Struktur .intern() und es zur Laufzeit durch Verkettung (und .intern() – Ergebnis) rekonstruieren. Ich habe keine Ahnung, ob ein Compiler das tatsächlich tut.


Es zeigt, dass sich das Problem nicht auf Zeichenfolgenliteralen, sondern auf Arrayinitialisierungen bezieht.

Wenn ein Objekt an BMethod.invoke (und ähnlich an BConstructor.newInstance) übergeben wird, kann es entweder ein BObject (dh ein Wrapper um ein vorhandenes Objekt herum sein, es wird dann das umgebrochene Objekt übergeben), ein String (der unverändert übergeben wird) ) oder irgendetwas anderes. Im letzten Fall wird das Objekt in eine Zeichenkette umgewandelt (von toString() ), und diese Zeichenkette wird dann als Java-Ausdruck interpretiert.

Um dies zu tun, wird BlueJ diesen Ausdruck in eine class / Methode einschließen und diese Methode kompilieren. In der Methode wird der Array-Initialisierer einfach in eine lange Liste von Array-Zuweisungen konvertiert … und dies macht die Methode schließlich länger als die maximale Bytecode-Größe einer Java-Methode:

Der Wert des Elements code_length muss kleiner als 65536 sein.

Deshalb bricht es für längere Arrays.


Um größere Arrays zu übergeben, müssen wir eine andere Möglichkeit finden, sie an BMethod.invoke zu übergeben. Die BlueJ-Erweiterungs-API hat keine Möglichkeit, in einem BObject eingepackte Arrays zu erstellen oder darauf zuzugreifen.

Eine Idee, die wir im Chat gefunden haben, ist diese:

  1. Erstellen Sie eine neue class innerhalb des Projekts (oder in einem neuen Projekt, wenn sie zusammenarbeiten können), etwa so:

     public class IntArrayBuilder { private ArrayList list; public void addElement(int el) { list.add(el); } public int[] makeArray() { int[] array = new int[list.size()]; for(int i = 0; i < array.length; i++) { array[i] = list.get(i); } return array; } } 

    (Dies ist für den Fall der Erstellung eines int[] - wenn Sie auch andere Arten von Array benötigen, kann es auch generischer gemacht werden. Es könnte auch effizienter gemacht werden, indem ein internes int[] als Speicher, Vergrößern verwendet wird Es sporadisch, wenn es wächst, und int makeArray macht eine letzte arraycopy. Dies ist eine Skizze, also ist dies die einfachste Implementierung.)

  2. Erstellen Sie ein Objekt dieser class aus unserer Erweiterung und fügen Sie diesem Objekt Elemente hinzu, indem .addElement Methode .addElement aufrufen.

     BObject arrayToBArray(int[] a) { BClass builderClass = package.getClass("IntArrayBuilder"); BObject builder = builderClass.getConstructor(new Class< ?>[0]).newInstance(new Object[0]); BMethod addMethod = builderClass.getMethod("addElement", new Class< ?>[]{int.class}); for(int e : a) { addMethod.invoke(builder, new Object[]{ e }); } BMethod makeMethod = builderClass.getMethod("addElement", new Class< ?>[0]); BObject bArray = (BObject)makeMethod.invoke(builder, new Object[0]); return bArray; } 

    (Aus Effizienzgründen können die BClass / BMethod-Objekte tatsächlich einmal abgerufen und für jede Array-Konvertierung nur einmal zwischengespeichert werden.)
    Wenn Sie die Array-Inhalte nach einem Algorithmus generieren, können Sie diese Generierung hier vornehmen, anstatt zuerst ein anderes Wrapping-Objekt zu erstellen.

  3. Rufen Sie in unserer Erweiterung die Methode auf, die wir tatsächlich mit dem langen Array aufrufen möchten, indem Sie unser umhülltes Array übergeben:

     Object result = method.invoke(obj, new Object[] { bArray }); 

Die String-Länge ist durch Integer.MAX_VALUE begrenzt

Wenn der Typ die Nummer max ist, length = Integer.MAX_VALUE, wenn type char max length = 65536 ist