¿Por qué esta sobrepase el límite de 65.535 bytes en los constructores de Java y los inicializadores estáticos?
Pregunta
exención de responsabilidad: Soy consciente de que puedo generar esta en tiempo de ejecución de Java, esto era necesario para un caso muy especial, mientras que el rendimiento probar algo de código. He encontrado un enfoque diferente, por lo que ahora esto es sólo más una curiosidad que otra práctica.
He intentado lo siguiente como un campo estático, como un campo de instancia, y se inicializa directamente dentro del constructor. Todos los eclipses tiempo me está informando que sea "El código de TestData constructor () está excediendo el límite de 65535 bytes" o "El código para el inicializador estático está excediendo el límite de 65535 bytes".
Hay 10.000 enteros. Si cada int es de 4 bytes (32bits), entonces ¿no sería 40.000 bytes? ¿Hay realmente más que 25.0000 bytes de sobrecarga, además de los datos sólo simplemente la construcción de la matriz?
Los datos se genera 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
Solución
Este es el código de bytes para la inicialización de 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 arreglo más grande, tanto el índice de matriz y el índice en el conjunto de constantes utilizarán 3 bytes para la mayoría de los elementos, lo que conduce a 8 bytes por elemento de la matriz. Así que para 10000 elementos que tendría que esperar aproximadamente 80 KB de código de bytes.
El código para inicializar grandes matrices con índices de 16 bits es el siguiente:
2016 dup
2017 sipush 298
2020 ldc_w <Integer 100298> [310]
2023 iastore
2024 dup
2025 sipush 299
2028 ldc_w <Integer 100299> [311]
Otros consejos
Los 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 bytes para cada número.
¿Por qué no mover 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 puede hacerse fácilmente mediante el uso de MyClass.class.getClassLoader().getResourceAsStream()
. Parece que esta donde debe estar, al menos.
O mejor aún, crear los valores aleatorios en el bloque inicializador estático utilizando las herramientas Java disponible. Y si necesita números repetibles "al azar", a continuación, sólo sembrar la instancia Random
con un fijo, pero seleccionadas al azar número cada vez.
Además de los valores de los números enteros, el constructor y el inicializador tiene que contener las instrucciones de la JVM para la carga de los números enteros en la matriz.
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 inicializa matrices de esta manera, pero no inicializar matrices grandes de manera eficiente.
Creo que el tamaño del código de caracteres es más de 65535. No es la memoria que emplea 10.000 enteros.
Creo que es posible que esta es la cantidad de memoria necesaria para representar los enteros de forma alfanumérica. Creo que se podría aplicar este límite para el código en sí, por lo que, cada int, por ejemplo:. 1494512072 realmente tiene 10 bytes (uno por dígitos) en lugar de sólo 4 bytes utilizados para la int32