2009-04-25 2 views
9

Haftungsausschluss: Ich weiß, dass ich dies zur Laufzeit in Java generieren kann, das war für einen ganz speziellen Fall während der Leistungsprüfung einige Code erforderlich. Ich habe eine andere Herangehensweise gefunden, also ist dies eher eine Kuriosität als irgendetwas Praktisches.Warum überschreitet das Limit von 65.535 Byte in Java-Konstruktoren und statischen Initialisierern?

Ich habe Folgendes als statisches Feld versucht, als Instanz Feld und direkt im Konstruktor initialisiert. Jedes Mal, wenn Eclipse mich darüber informiert, dass entweder der Code des Konstruktors TestData() den Grenzwert von 65535 Bytes überschreitet oder der Code für den statischen Initialisierer den Grenzwert von 65535 Bytes überschreitet.

Es gibt 10.000 ganze Zahlen. Wenn jedes int 4 Byte (32 Bit) ist, wären das nicht 40.000 Byte? Gibt es wirklich mehr als 25.000 Bytes Overhead zusätzlich zu den Daten, die nur das Array aufbauen?

Die Daten werden mit diesem kleinen Stück Python generiert:

#!/usr/bin/python 

import random; 
print "public final int[] RANDOM_INTEGERS = new int[] {"; 
for i in range(1,10000): 
    print str(int(random.uniform(0,0x7fffffff))) + ","; 
print "};"; 

Hier ist eine kleine Auswahl:

public final int[] RANDOM_INTEGERS = new int[] { 
    963056418, 460816633, 1426956928, 1836901854, 334443802, 721185237, 488810483, 
    1734703787, 1858674527, 112552804, 1467830977, 1533524842, 1140643114, 1452361499, 
    716999590, 652029167, 1448309605, 1111915190, 1032718128, 1194366355, 112834025, 
    419247979, 944166634, 205228045, 1920916263, 1102820742, 1504720637, 757008315, 
    67604636, 1686232265, 597601176, 1090143513, 205960256, 1611222388, 1997832237, 
    1429883982, 1693885243, 1987916675, 159802771, 1092244159, 1224816153, 1675311441, 
    1873372604, 1787757434, 1347615328, 1868311855, 1401477617, 508641277, 1352501377, 
    1442984254, 1468392589, 1059757519, 1898445041, 1368044543, 513517087, 99625132, 
    1291863875, 654253390, 169170318, 2117466849, 1711924068, 564675178, 208741732, 
    1095240821, 1993892374, 87422510, 1651783681, 1536657700, 1039420228, 674134447, 
    1083424612, 2137469237, 1294104182, 964677542, 1506442822, 1521039575, 64073383, 
    929517073, 206993014, 466196357, 1139633501, 1692533218, 1934476545, 2066226407, 
    550646675, 624977767, 1494512072, 1230119126, 1956454185, 1321128794, 2099617717, 
    //.... to 10,0000 instances 
+0

Beim Nachschlagen finde ich, dass diese Grenze für alle Methoden (einschließlich Konstruktoren) sowie statische Initialisierer gilt. Interessant! –

+0

http://groups.google.com/group/comp.lang.java.machine/browse_thread/thread/b0cf268515f1ef55 –

Antwort

13

ist der Bytecode zur Initialisierung eines Arrays mit {1000001, 1000002, 1000003}:

5 iconst_3 
6 newarray int [10] 
8 dup 
9 iconst_0 
10 ldc <Integer 1000001> [12] 
12 iastore 
13 dup 
14 iconst_1 
15 ldc <Integer 1000002> [13] 
17 iastore 
18 dup 
19 iconst_2 
20 ldc <Integer 1000003> [14] 
22 iastore 
23 putfield net.jstuber.test.TestArrayInitializingConstructor.data : int[] [15] 

Also, für dieses kleine Array jedes Element erfordert 5 Byte Java-Bytecode. Für Ihr größeres Array verwenden sowohl der Array-Index als auch der Index im Konstanten-Pool 3 Bytes für die meisten Elemente, was zu 8 Bytes pro Array-Element führt. Also für 10000 Elemente müssten Sie ungefähr 80kB Byte-Code erwarten.

Der Code für große Arrays mit 16-Bit-Indizes initialisiert sieht wie folgt aus:

2016 dup 
2017 sipush 298 
2020 ldc_w <Integer 100298> [310] 
2023 iastore 
2024 dup 
2025 sipush 299 
2028 ldc_w <Integer 100299> [311] 
+0

Werden String-Literale speziell behandelt? Sind sie das einzige, was ist? Wäre es sinnvoll, Arrays zu initialisieren, indem Sie die Daten in String-Literale packen (vielleicht für "int" -Werte, die Zahlen +/- 16383 als ein Zeichen speichern, +/- 268435455 oder bestimmte andere ausgewählte Werte als zwei und alles speichern) sonst wie drei]? – supercat

+0

@supercat Ja, das könnte funktionieren. Jede Zeichenfolge verwendet zwei konstante Pool-Indizes, einen für die Zeichenfolge (http://docs.oracle.com/javase/specs/jvms/se7/html/jvms-4.html#jvms-4.4.3) und einen für die aktuelle UTF-8-Daten (http://docs.oracle.com/javase/specs/jvms/se7/html/jvms-4.html#jvms-4.4.7). Soweit ich sehen kann, gibt es keinen anderen konstanten Typ von etwas beliebiger Größe (siehe 4.4 Der konstante Pool http://docs.oracle.com/javase/specs/jvms/se7/html/jvms-4.html#jvms- 4.4). Obwohl ich lieber eine Ressource als einen solchen verwenden würde. – starblue

-4

Ich denke, es ist möglich, dass dies die Größe des Speichers, diese ints darzustellen erforderlich ist alphanumerisch. Ich denke, dass diese Grenze für den Code selbst gelten kann, so dass jedes int, zum Beispiel: 1494512072 tatsächlich 10 Bytes (eins pro Ziffer) statt nur 4 Bytes für das int32 verwendet.

+0

Ich bin ziemlich sicher, dass der "Code", auf den in der Fehlermeldung verwiesen wird, sich auf den generierten Bytecode bezieht. –

0

Ich denke, dass die Code-Größe in Zeichen mehr als 65535 ist. Nicht die Speicherkapazität von 10000 Ganzzahlen.

+0

11 Zeichen mal 10.000 Einträge sind 110.000 Bytes, mehr oder weniger. Absolut über dem Limit –

+1

Warum wurde diese Antwort abgelehnt? Es ist korrekt, auf eine minimalistische Art und Weise. –

3

Neben den Werten der Ganzzahlen müssen der Konstruktor und der Initialisierer die JVM-Anweisungen zum Laden der Ganzzahlen in das Array enthalten.

+0

Also ich denke, das ist, was ich erwartet habe, nur überraschend ein Array literal Init-Code ist> 25.000 Bytes (Ich bin sicher, es gibt einige kleine Overhead in der Einrichtung der Klasse/Methode/etc). –

+1

Sie können javap verwenden, um zu sehen, was vor sich geht. – TrayMan

6

Array-Literale werden in den Byte-Code übersetzt, der das Array mit den Werten füllt, sodass Sie für jede Zahl ein paar Bytes mehr benötigen.

Warum verschieben Sie diese Daten nicht in eine Ressource, die Sie zum Laden der Klasse in einem statischen Initialisierungsblock laden? Dies kann einfach unter Verwendung von MyClass.class.getClassLoader().getResourceAsStream() durchgeführt werden. Es scheint, dass das hier wo es hingehört.

Oder noch besser, erstellen Sie die zufälligen Werte im statischen Initialisierungsblock mit den verfügbaren Java-Tools. Und wenn Sie wiederholbare "zufällige" Zahlen benötigen, dann setzen Sie einfach die Random Instanz mit einer festen, aber zufällig gewählten Nummer jedes Mal. Hier

+2

Die zu testende Umgebung erlaubt keinen Datei-E/A-Zugriff. –

+2

Aber Sie laden Klassen, also können Sie wahrscheinlich MyClass.class.getResourceAsStream() tun und laden Sie es aus dem Jar, in dem Sie Ihre Anwendung packen. Das sollte * immer * möglich sein. –

1

Eine viel einfacher und praktischer Ansatz ist es, die Zahlen in einer Datei zu speichern, entweder in einem binären Format oder als Text.

Ich weiß nicht, was Java auf diese Weise initialisiert, aber es initialisiert große Arrays nicht effizient.

Verwandte Themen