2009-04-25 11 views
9

Descargo de responsabilidad: me doy cuenta de que puedo generar esto en tiempo de ejecución en Java, esto fue necesario para un caso muy especial mientras el rendimiento probaba algún código. He encontrado un enfoque diferente, así que ahora esto es más una curiosidad que cualquier cosa práctica.¿Por qué excede el límite de 65.535 bytes en los constructores de Java y los inicializadores estáticos?

He intentado lo siguiente como un campo estático, como un campo de instancia, y se inicializa directamente dentro del constructor. Cada vez que eclipse me informa que "el código del constructor TestData() excede el límite de 65535 bytes" o "el código para el inicializador estático excede el límite de 65535 bytes".

Hay 10.000 enteros. Si cada int es 4 bytes (32bits), ¿no serían 40,000 bytes? ¿Hay realmente más de 25,000 bytes de sobrecarga, además de los datos simplemente construyendo la matriz?

Los datos son generados con este pequeño fragmento de pitón:

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

He aquí una pequeña muestra:

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

Al leer esto, encuentro que este límite se aplica a todos los métodos (incluidos los constructores), así como a los inicializadores estáticos. ¡Interesante! –

+0

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

Respuesta

13

Aquí está el código de bytes para inicializar una matriz con {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] 

Así, por esta pequeña gama cada elemento requiere 5 bytes de código de bytes de Java. Para su matriz más grande, tanto el índice de matriz como el índice en el grupo de constante usarán 3 bytes para la mayoría de los elementos, lo que lleva a 8 bytes por elemento de matriz. Por lo tanto, para 10000 elementos, debe esperar aproximadamente 80kB de código de bytes.

El código para inicializar grandes matrices con índices de 16 bits tiene el siguiente aspecto:

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

¿Se manejan especialmente los literales de cadena? ¿Son ellos lo único que es? ¿Tendría sentido inicializar matrices empacando los datos en cadenas literales [quizás diciendo, para valores 'int', que los números +/- 16383 almacenan como un carácter, +/- 268435455 o ciertos otros valores de selección almacenados como dos, y cualquier cosa más como tres]? – supercat

+0

@supercat Sí, eso podría funcionar. Cada cadena utiliza dos índices de grupo constantes, uno para la cadena (http://docs.oracle.com/javase/specs/jvms/se7/html/jvms-4.html#jvms-4.4.3) y otro para el datos UTF-8 reales (http://docs.oracle.com/javase/specs/jvms/se7/html/jvms-4.html#jvms-4.4.7). Por lo que puedo ver, no hay otro tipo constante de tamaño algo arbitrario (ver 4.4 The Constant Pool http://docs.oracle.com/javase/specs/jvms/se7/html/jvms-4.html#jvms- 4.4). Aunque preferiría usar un recurso que ese hack. – starblue

-4

Creo que es posible que esta es la cantidad de memoria necesaria para representar los enteros alfanuméricamente. Creo que este límite podría aplicarse al código en sí, así que, cada int, por ejemplo: 1494512072 en realidad toma 10 bytes (uno por dígito) en lugar de solo 4 bytes utilizados para el int32.

+0

Estoy bastante seguro de que el "código" al que se hace referencia en el mensaje de error se refiere al bytecode generado. –

0

Creo que el tamaño del código en caracteres es más de 65535. No es la memoria tomada por 10000 enteros.

+0

11 caracteres por cada 10.000 entradas es 110,000 bytes, más o menos. Absolutamente por encima del límite –

+1

¿Por qué se votó negativamente esta respuesta? Es correcto, de una manera minimalista. –

3

Además de los valores de los números enteros, el constructor y el inicializador debe contener las instrucciones de JVM para la carga de los números enteros en la matriz.

+0

Así que supongo que esto es lo que esperaba, solo me sorprende una matriz código de inicio literal es> 25,000 bytes (estoy seguro de que hay una pequeña sobrecarga en la configuración de la clase/método/etc.). –

+1

Puede usar javap para ver qué está pasando. – TrayMan

6

literales de conjunto se traducen en el código de bytes que se llena la matriz con los valores, por lo que necesita un poco más de bytes para cada número.

¿Por qué no se mueven los datos a cabo en un recurso que se carga en el momento de la clase de carga en un bloque inicializador estático? Esto se puede hacer fácilmente usando MyClass.class.getClassLoader().getResourceAsStream(). Parece que esto es donde pertenece, de todos modos.

O mejor aún, cree los valores aleatorios en el bloque de inicializador estático utilizando las herramientas de Java disponibles. Y si necesita números repetibles "aleatorios", solo inserte la instancia Random con un número fijo, pero elegido al azar cada vez.

+2

El entorno que se está probando no permite el acceso de E/S de archivo. –

+2

Pero está cargando clases, por lo que probablemente pueda hacer MyClass.class.getResourceAsStream() y cargarlo desde el contenedor en el que empaqueta su aplicación. Eso debería * siempre * ser posible. –

1

Un enfoque mucho más simple y más práctico es almacenar los números en un archivo, ya sea en un formato binario o como texto.

No sé qué Java inicia las matrices de esta manera, pero no inicializa las matrices de gran tamaño de manera eficiente.

Cuestiones relacionadas