java - ¿Por qué no puedo crear una matriz de gran tamaño?
jvm jvm-hotspot (5)
Teoría
Hay dos posibles excepciones:
-
OutOfMemoryError: Java heap space
significa que su matriz no cabe en el espacio deOutOfMemoryError: Java heap space
. Para resolverlo, puede aumentar el tamaño máximo de-Xmx
dinámico utilizando la opción JVM-Xmx
. También tenga en cuenta que el tamaño máximo del objeto no puede ser mayor que la generación de almacenamiento dinámico más grande . -
OutOfMemoryError: Requested array size exceeds VM limit
significa que se excedió el tamaño específico de la plataforma:-
el límite superior se establece mediante las restricciones del tipo de tamaño utilizado para describir un índice en la matriz, por lo que el tamaño teórico de la matriz está limitado por
2^31-1=2147483647
elementos. - el otro límite es JVM / plataforma específica. De acuerdo con el capítulo 10: Matrices de The Java Language Specification, Java SE 7 Edition, no hay un límite estricto en la longitud de la matriz, por lo tanto, el tamaño de la matriz puede reducirse sin violar JLS.
-
el límite superior se establece mediante las restricciones del tipo de tamaño utilizado para describir un índice en la matriz, por lo que el tamaño teórico de la matriz está limitado por
Práctica
En HotSpot, el tamaño de la matriz JVM está limitado por la representación interna.
En el código GC, JVM pasa alrededor del tamaño de una matriz en palabras de montón, ya que un
int
vuelve a convertir de palabras de montón a
jint
esto puede causar un desbordamiento.
Entonces, para evitar bloqueos y comportamientos inesperados, la longitud máxima de la matriz está limitada por
(tamaño máximo - tamaño del encabezado)
.
Donde el
tamaño del encabezado
depende del compilador C / C ++ que se utilizó para compilar la JVM que está ejecutando (gcc para linux, clang para macos) y la configuración del tiempo de ejecución (como
UseCompressedClassPointers
).
Por ejemplo en mi linux:
-
Java HotSpot (TM) 64-Bit Server VM 1.6.0_45 limit
Integer.MAX_VALUE
-
Java HotSpot (TM) 64-Bit Server VM 1.7.0_72 limit
Integer.MAX_VALUE-1
-
Java HotSpot (TM) 64-Bit Server VM 1.8.0_40 limit
Integer.MAX_VALUE-2
Enlaces útiles
¿Por qué es imposible crear una matriz con un tamaño máximo de int?
int i = 2147483647;
int[] array = new int[i];
Encontré esta explicación:
Se accede a las matrices Java a través de entradas de 32 bits, lo que da como resultado un tamaño de matriz teórica máximo de 2147483647 elementos.
Pero como puedes ver, mi código no funciona. También es imposible crear una matriz con tamaño
new int[Integer.MAX_VALUE - 5];
Detalles técnicos
- JVM HotSpot de 64 bits
- OSX 10.10.4
PD
¿Y por qué
-5
realidad?
Algunas máquinas virtuales reservan algunas palabras de encabezado en una matriz.
El número máximo "seguro" sería
be 2 147 483 639 (Integer.MAX_VALUE - 8)
Source-http://www.docjar.com/html/api/java/util/ArrayList.java.html
**
191 * The maximum size of array to allocate.
192 * Some VMs reserve some header words in an array.
193 * Attempts to allocate larger arrays may result in
194 * OutOfMemoryError: Requested array size exceeds VM limit
195 */
196 private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
Entonces, depende de la memoria máxima disponible para su JVM en su SYSTEM NOW
Editar: ¿Por qué se muestra OOM?
Número de elementos = 2147483639
cantidad de bytes necesarios para un elemento = 4
Memoria total para solo Element 8589934556 KB == 8.589934555999999 GB
Ahora, si el uso de memoria total de la matriz no es un múltiplo de 8 bytes, entonces el tamaño se redondea al siguiente conjunto de 8.
Por lo tanto, necesita más de lo que está asignando debido a los gastos generales también y eso debe ser memoria continua
Bueno, Ivan ya ha señalado correctamente que la longitud de la matriz tiene un límite superior bien definido y que nuevamente depende de JVM / Platform. De hecho, lo que es más importante, también afirmó que la cantidad de matriz que realmente puede crear en su código se controlará principalmente por la cantidad de espacio de almacenamiento dinámico máximo que haya asignado a su programa durante la ejecución.
Solo me gustaría agregar un pequeño fragmento de código para respaldar su explicación. Por ejemplo, teóricamente, una matriz [] debería aceptar la longitud <= INTEGER.MAX_VALUE - x (aquí x es el tamaño del encabezado que nuevamente es JVM / Plataforma específica) pero suponga que ejecuta el siguiente programa Java con la opción VM -Xmx32m, entonces verá que ninguno de los arreglos creados alcanza la longitud cercana a MAX_ARRAY_SIZE (es decir, 2147483639)
byte [] array: 1 byte
0 l = 1048576 s = 1mb
1 l = 2097152 s = 2mb
2 l = 4194304 s = 4mb
3 l = 8388608 s = 8mb
java.lang.OutOfMemoryError: espacio de almacenamiento dinámico Java l = 16777216 s = 16mb
matriz char []: 2 bytes
0 l = 1048576 s = 2mb
1 l = 2097152 s = 4mb
2 l = 4194304 s = 8mb
java.lang.OutOfMemoryError: espacio de almacenamiento dinámico Java l = 8388608 s = 16mb
int [] array: 4 bytes
0 l = 1048576 s = 4mb
1 l = 2097152 s = 8mb
java.lang.OutOfMemoryError: espacio de almacenamiento dinámico Java l = 4194304 s = 16mb
matriz doble []: 8 bytes
0 l = 1048576 s = 8mb
java.lang.OutOfMemoryError: espacio de almacenamiento dinámico Java l = 2097152 s = 16mb
Debajo está el código:
byte[] barray = null;
System.out.println("/nbyte[] array : 1 byte");
try {
for (ii=0; ii < 32; ii++) {
barray = new byte[(int)Math.pow(2, ii)*1024*1024];
System.out.println(ii + " l=" + barray.length + " s=" + barray.length / (1024 * 1024) + "mb");
}
}
catch (Throwable e) {
barray = null;
System.out.println(e + " l=" + (int)Math.pow(2, ii)*1024*1024 + " s=" + (int)Math.pow(2, ii)*1024*1024 / (1024 * 1024) + "mb");
}
char[] carray = null;
System.out.println("/nchar[] array : 2 byte");
try {
for (ii=0; ii < 32; ii++) {
carray = new char[(int)Math.pow(2, ii)*1024*1024];
System.out.println(ii + " l=" + carray.length + " s=" + 2*carray.length / (1024 * 1024) + "mb");
}
}
catch (Throwable e) {
carray = null;
System.out.println(e + " l=" + (int)Math.pow(2, ii)*1024*1024 + " s=" + 2*(int)Math.pow(2, ii)*1024*1024 / (1024 * 1024) + "mb");
}
int[] iarray = null;
System.out.println("/nint[] array : 4 byte");
try {
for (ii=0; ii < 32; ii++) {
iarray = new int[(int)Math.pow(2, ii)*1024*1024];
System.out.println(ii + " l=" + iarray.length + " s=" + 4*iarray.length / (1024 * 1024) + "mb");
}
}
catch (Throwable e) {
iarray = null;
System.out.println(e + " l=" + (int)Math.pow(2, ii)*1024*1024 + " s=" + 4*(int)Math.pow(2, ii)*1024*1024 / (1024 * 1024) + "mb");
}
double[] darray = null;
System.out.println("/ndouble[] array : 8 byte");
try {
for (ii=0; ii < 32; ii++) {
darray = new double[(int)Math.pow(2, ii)*1024*1024];
System.out.println(ii + " l=" + darray.length + " s=" + 8*darray.length / (1024 * 1024) + "mb");
}
}
catch (Throwable e) {
darray = null;
System.out.println(e + " l=" + (int)Math.pow(2, ii)*1024*1024 + " s=" + 8*(int)Math.pow(2, ii)*1024*1024 / (1024 * 1024) + "mb");
}
No es suficiente tener suficiente montón para esa asignación; debe tener una sola región de montón de tamaño suficiente. Como sabes, el montón se divide en generaciones.
Para una única asignación de 8 GB, debe asegurarse de eso para una sola región de almacenamiento dinámico (más algunos gastos generales).
Con 12 GB de
-Xmx
aún puede ser corto.
Use opciones adicionales para controlar el tamaño de la generación anterior.
encuentre su tamaño máximo de almacenamiento dinámico yendo a cmd e ingrese esta línea
javaw -XX:+PrintFlagsFinal | find "MaxHeapSize"
y luego divídalo por
1.5
, obtendrá el tamaño máximo aproximado de la matriz para su computadora