Matriz de Java con elementos de más de 4 gb

Tengo un archivo grande, se espera que sea de alrededor de 12 GB. Quiero cargar todo en la memoria en una máquina robusta de 64 bits con 16 GB de RAM, pero creo que Java no admite matrices de bytes tan grandes:

File f = new File(file); long size = f.length(); byte data[] = new byte[size]; // <- does not compile, not even on 64bit JVM 

¿Es posible con Java?

El error de comstackción del comstackdor de Eclipse es:

 Type mismatch: cannot convert from long to int 

javac da:

 possible loss of precision found : long required: int byte data[] = new byte[size]; 

Los índices de matriz Java son de tipo int (4 bytes o 32 bits), así que me temo que está limitado a 2 31 – 1 o 2147483647 ranuras en su matriz. Leí los datos en otra estructura de datos, como una matriz 2D.

 package com.deans.rtl.util; import java.io.FileInputStream; import java.io.IOException; /** * * @author william.deans@gmail.com * * Written to work with byte arrays requiring address space larger than 32 bits. * */ public class ByteArray64 { private final long CHUNK_SIZE = 1024*1024*1024; //1GiB long size; byte [][] data; public ByteArray64( long size ) { this.size = size; if( size == 0 ) { data = null; } else { int chunks = (int)(size/CHUNK_SIZE); int remainder = (int)(size - ((long)chunks)*CHUNK_SIZE); data = new byte[chunks+(remainder==0?0:1)][]; for( int idx=chunks; --idx>=0; ) { data[idx] = new byte[(int)CHUNK_SIZE]; } if( remainder != 0 ) { data[chunks] = new byte[remainder]; } } } public byte get( long index ) { if( index<0 || index>=size ) { throw new IndexOutOfBoundsException("Error attempting to access data element "+index+". Array is "+size+" elements long."); } int chunk = (int)(index/CHUNK_SIZE); int offset = (int)(index - (((long)chunk)*CHUNK_SIZE)); return data[chunk][offset]; } public void set( long index, byte b ) { if( index<0 || index>=size ) { throw new IndexOutOfBoundsException("Error attempting to access data element "+index+". Array is "+size+" elements long."); } int chunk = (int)(index/CHUNK_SIZE); int offset = (int)(index - (((long)chunk)*CHUNK_SIZE)); data[chunk][offset] = b; } /** * Simulates a single read which fills the entire array via several smaller reads. * * @param fileInputStream * @throws IOException */ public void read( FileInputStream fileInputStream ) throws IOException { if( size == 0 ) { return; } for( int idx=0; idx 

Si es necesario, puede cargar los datos en una matriz de matrices, lo que le dará un máximo de int.maxValue squared bytes, más de lo que incluso la máquina de mayor tamaño podría tener en la memoria.

Sugiero que defina algunos objetos de “bloque”, cada uno de los cuales contiene (digamos) 1 Gb en una matriz, luego haga una matriz de esos.

No, las matrices están indexadas por int s (excepto algunas versiones de JavaCard que usan s short ). Tendrá que dividirlo en matrices más pequeñas, probablemente envolviendo un tipo que le proporcione get(long) , set(long,byte) , etc. Con secciones de datos tan grandes, es posible que desee mapear el archivo use java. nio.

Considere usar FileChannel y MappedByteBuffer para mapear la memoria del archivo,

 FileChannel fCh = new RandomAccessFile(file,"rw").getChannel(); long size = fCh.size(); ByteBuffer map = fCh.map(FileChannel.MapMode.READ_WRITE, 0, fileSize); 

Editar:

Ok, soy un idiota, parece que ByteBuffer solo toma un índice de 32 bits, lo que es extraño ya que el parámetro de tamaño para FileChannel.map es largo … Pero si decides dividir el archivo en múltiples fragmentos de 2Gb para la carga, aún recomiendo IO con memoria asignada, ya que puede haber beneficios de rendimiento bastante grandes. Básicamente estás moviendo toda la responsabilidad IO al kernel del sistema operativo.

Los arrays de Java usan enteros para sus índices. Como resultado, el tamaño máximo de la matriz es Integer.MAX_VALUE.

(Desafortunadamente, no puedo encontrar ninguna prueba por parte de Sun acerca de esto, pero ya hay muchas discusiones en sus foros al respecto).

Creo que la mejor solución que podría hacer mientras tanto sería hacer una matriz 2D, es decir:

 byte[][] data; 

Como han dicho otros, todas las matrices de Java de todos los tipos están indexadas por int , y también pueden ser de tamaño máximo 2 31 – 1 o 2147483647 elementos (~ 2 mil millones). Esto está especificado en la Especificación del lenguaje Java, por lo que cambiar a otro sistema operativo o Máquina virtual Java no será de ayuda.

Si quisiera escribir una clase para superar esto como se sugirió anteriormente, podría usar una matriz de matrices (para mucha flexibilidad) o cambiar tipos (una long es de 8 bytes, por lo que una long[] puede ser 8 veces mayor que un byte[] ).

Creo que la idea de mapeo de memoria del archivo (utilizando el hardware de memoria virtual de la CPU) es el enfoque correcto. Excepto que MappedByteBuffer tiene la misma limitación de 2Gb que las matrices nativas. Este tipo afirma haber resuelto el problema con una alternativa bastante simple a MappedByteBuffer:

http://nyeggen.com/post/2014-05-18-memory-mapping-%3E2gb-of-data-in-java/

https://gist.github.com/bnyeggen/c679a5ea6a68503ed19f#file-mmapper-java

Lamentablemente, la JVM se bloquea cuando lee más allá de 500Mb.

no te limites con Integer.MAX_VALUE

aunque esta pregunta se ha hecho hace muchos años, pero ai quería participar con un ejemplo simple usando solo java se sin ninguna biblioteca externa

al principio digamos que es teóricamente imposible pero prácticamente posible

una nueva apariencia : si la matriz es un objeto de elementos, ¿qué pasa con tener un objeto que es una matriz de matrices

aquí está el ejemplo

 import java.lang.reflect.Array; import java.util.ArrayList; import java.util.List; /** * * @author Anosa */ public class BigArray{ private final static int ARRAY_LENGTH = 1000000; public final long length; private List arrays; public BigArray(long length, Class glasss) { this.length = length; arrays = new ArrayList<>(); setupInnerArrays(glasss); } private void setupInnerArrays(Class glasss) { long numberOfArrays = length / ARRAY_LENGTH; long remender = length % ARRAY_LENGTH; /* we can use java 8 lambdas and streams: LongStream.range(0, numberOfArrays). forEach(i -> { arrays.add((t[]) Array.newInstance(glasss, ARRAY_LENGTH)); }); */ for (int i = 0; i < numberOfArrays; i++) { arrays.add((t[]) Array.newInstance(glasss, ARRAY_LENGTH)); } if (remender > 0) { //the remainer will 100% be less than the [ARRAY_LENGTH which is int ] so //no worries of casting (: arrays.add((t[]) Array.newInstance(glasss, (int) remender)); } } public void put(t value, long index) { if (index >= length || index < 0) { throw new IndexOutOfBoundsException("out of the reange of the array, your index must be in this range [0, " + length + "]"); } int indexOfArray = (int) (index / ARRAY_LENGTH); int indexInArray = (int) (index - (indexOfArray * ARRAY_LENGTH)); arrays.get(indexOfArray)[indexInArray] = value; } public t get(long index) { if (index >= length || index < 0) { throw new IndexOutOfBoundsException("out of the reange of the array, your index must be in this range [0, " + length + "]"); } int indexOfArray = (int) (index / ARRAY_LENGTH); int indexInArray = (int) (index - (indexOfArray * ARRAY_LENGTH)); return arrays.get(indexOfArray)[indexInArray]; } 

}

y aquí está la prueba

 public static void main(String[] args) { long length = 60085147514l; BigArray array = new BigArray<>(length, String.class); array.put("peace be upon you", 1); array.put("yes it worj", 1755); String text = array.get(1755); System.out.println(text + " i am a string comming from an array "); } 

este código está limitado solo por Long.MAX_VALUE y el montón de Java, pero puede excederlo como desee (lo hice 3800 MB)

Espero que esto sea útil y proporcione una respuesta simple

java no es compatible con matriz directa con más de 2 ^ 32 elementos actualmente,

espero ver esta característica de Java en el futuro

    Intereting Posts