2009-04-25 12 views
9

Disclaimer: Mi rendo conto che è possibile generare questo in fase di esecuzione in Java, questo era necessario per un caso molto particolare mentre le prestazioni testavano un po 'di codice. Ho trovato un approccio diverso, quindi ora questa è solo una curiosità più che pratica.Perché supera il limite di 65.535 byte nei costruttori Java e negli inizializzatori statici?

Ho provato quanto segue come campo statico, come campo di istanza, e inizializzato direttamente all'interno del costruttore. Ogni volta che eclipse mi informa che "Il codice del costruttore TestData() sta superando il limite di 65535 byte" o "Il codice per l'inizializzatore statico supera il limite di 65535 byte".

Ci sono 10.000 numeri interi. Se ogni int è 4 byte (32 bit), quindi non sarebbe 40.000 byte? C'è davvero più di 25,0000 byte di overhead in aggiunta ai dati semplicemente costruendo la matrice?

I dati viene generato con questo piccolo pezzo di pitone:

#!/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 "};"; 

Ecco un piccolo esempio:

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

Nella lettura di questo, trovo che questo limite si applica a tutti i metodi (compresi i costruttori) e agli inizializzatori statici. Interessante! –

+0

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

risposta

13

Ecco il bytecode per inizializzare un array con {1.000.001, 1.000.002, 1.000.003}:

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] 

Così, per questa piccola serie ogni elemento richiede 5 byte di bytecode Java. Per l'array più grande, sia l'indice dell'array che l'indice nel pool costante utilizzeranno 3 byte per la maggior parte degli elementi, il che porta a 8 byte per elemento di array. Quindi per 10000 elementi dovresti aspettarti circa 80kB di byte code.

Il codice per l'inizializzazione grandi array con gli indici a 16 bit si presenta così:

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

I letterali stringa sono gestiti in modo speciale? Sono l'unica cosa che è? Avrebbe senso inizializzare gli array comprimendo i dati in stringhe letterali [forse dicendo, per i valori di 'int', che i numeri +/- 16383 memorizzano come un carattere, +/- 268435455 o alcuni altri valori selezionati vengono memorizzati come due, e qualsiasi cosa altro come tre]? – supercat

+0

@supercat Sì, potrebbe funzionare. Ogni stringa utilizza due indici di pool costanti, uno per la stringa (http://docs.oracle.com/javase/specs/jvms/se7/html/jvms-4.html#jvms-4.4.3) e uno per il dati UTF-8 effettivi (http://docs.oracle.com/javase/specs/jvms/se7/html/jvms-4.html#jvms-4.4.7). Per quanto posso vedere, non esiste un altro tipo costante di dimensione alquanto arbitraria (si veda 4.4 Il pool costante http://docs.oracle.com/javase/specs/jvms/se7/html/jvms-4.html#jvms- 4.4). Anche se preferirei usare una risorsa piuttosto che un simile trucco. – starblue

-4

Penso che sia possibile che questa è la quantità di memoria necessaria per rappresentare quei int alfanumerico. Penso che questo limite potrebbe applicarsi al codice stesso, quindi, ogni int, ad esempio: 1494512072 richiede effettivamente 10 byte (uno per cifra) anziché solo 4 byte utilizzati per l'int32.

+0

Sono abbastanza sicuro che il "codice" a cui si fa riferimento nel messaggio di errore si riferisca al bytecode generato. –

0

Penso che la dimensione del codice in caratteri sia superiore a 65535. Non la memoria presa da 10000 numeri interi.

+0

11 caratteri per 10.000 voci è 110.000 byte, più o meno. Assolutamente oltre il limite –

+1

Perché questa risposta è stata downvoted? È corretto, in un modo minimalista. –

3

Oltre ai valori degli interi, il costruttore e l'inizializzatore devono contenere le istruzioni JVM per caricare gli interi nell'array.

+0

Quindi immagino che questo è quello che mi aspettavo, solo un codice init letterale array è> 25.000 byte (sono sicuro che c'è un piccolo overhead nell'impostazione della classe/metodo/etc). –

+1

Puoi usare javap per vedere cosa sta succedendo. – TrayMan

6

I valori letterali di array vengono convertiti nel codice byte che riempie l'array con i valori, quindi sono necessari pochi byte in più per ciascun numero.

Perché non spostare tali dati in una risorsa che si carica in fase di caricamento in un blocco di inizializzazione statico? Questo può essere fatto facilmente usando MyClass.class.getClassLoader().getResourceAsStream(). Sembra che questo sia dove appartiene, comunque.

O meglio ancora, creare i valori casuali nel blocco di inizializzazione statico utilizzando gli strumenti Java disponibili. E se hai bisogno di numeri "casuali" ripetibili, semina l'istanza Random con un numero fisso, ma casualmente scelto ogni volta.

+2

L'ambiente sottoposto a test non consente l'accesso I/O file. –

+2

Ma stai caricando le classi, quindi probabilmente puoi fare MyClass.class.getResourceAsStream() e caricarlo dal barattolo in cui impacchetta l'applicazione. Ciò dovrebbe * sempre * essere possibile. –

1

Un approccio molto più semplice e più pratico è quello di memorizzare i numeri in un file, sia in formato binario o come testo.

Non so cosa java inizializzi gli array in questo modo, ma non inizializza in modo efficiente gli array di grandi dimensioni.

Problemi correlati