2009-05-18 9 views
17

J'ai un gros fichier, il devrait être d'environ 12 GB. Je veux charger tout en mémoire sur une machine 64 bits costaud avec 16 Go de RAM, mais je pense que Java ne supporte pas les tableaux d'octets que grand:Java array avec plus de 4gb éléments

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

Est-il possible avec Java?

L'erreur de compilation du compilateur Eclipse est:

Type mismatch: cannot convert from long to int 

javac donne:

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

Juste curieux: Pourquoi avez-vous besoin de garder autant de données en mémoire en même temps? Ne serait-il pas possible de diviser cela en morceaux? –

+1

+1 au commentaire de bruno. La seule façon d'avoir le fichier entier en mémoire sera un avantage si vous avez besoin de faire des accès aléatoires dans différents points du fichier, et dans ce cas vous seriez certainement mieux de l'analyser dans une représentation plus computable. – kdgregory

+0

Je suis va essayer d'utiliser un arbre de préfixe (trie) pour garder les données, cela peut le rétrécir assez pour tenir dans 2 Go de mémoire. –

Répondre

18

Java indices de tableau sont de type int (4 octets ou 32 bits), donc je suis peur vous êtes limité à 2 - 1 ou 2147483647 emplacements dans votre tableau. Je lisais les données dans une autre structure de données, comme un tableau 2D.

+0

merci à tous. Bill a eu la réponse en premier. –

+0

@OmryYadan, La [limite réelle sera en fait moins] (http://stackoverflow.com/questions/3038392/do-java-arrays-have-a-maximum-size/8381338#comment45805541_3039805) que 2147483647. – Pacerier

+0

vous voulez dire MAX_INT - 8? https: // github.com/omry/banane/blob/1621638d6eb4db773045af66eac66be0fffa91fa/banane/src/net/yadan/banane/mémoire/bloc/BigBlockAllocator.java # L25 –

6

Si nécessaire, vous pouvez charger les données dans un tableau de tableaux, ce qui vous donnera un maximum de 0maxValeur au carré octets, plus que même la machine la plus solide serait bien en mémoire.

+0

ce serait ma prochaine étape. puisque j'ai l'intention de faire une recherche binaire sur les données, cela va grossir le code, mais je crains qu'il n'y ait pas de choix. –

+0

Vous pouvez créer une classe qui gère un tableau de tableaux mais fournit une abstraction similaire à un tableau régulier, par exemple avec get et set qui prennent un index long. –

2

Je vous suggère de définir des objets "bloc", dont chacun contient (disons) 1Gb dans un tableau, puis en faire un tableau.

1

Les tableaux Java utilisent des entiers pour leurs indices. Par conséquent, la taille maximale du tableau est Integer.MAX_VALUE.

(Malheureusement, je ne trouve aucune preuve de Sun se à ce sujet, mais il y a plenty de discussions sur leur forums à ce sujet déjà.)

Je pense que vous pourriez faire la meilleure solution dans l'intervalle serait être de faire un tableau 2D, à savoir:

byte[][] data; 
2

Non, les tableaux sont indexés par int s (sauf certaines versions de JavaCard qui utilisent short s). Vous aurez besoin de le découper en plus petits tableaux, en l'enveloppant probablement dans un type qui vous donne get(long), set(long,byte), etc. Avec des sections de données de cette taille, vous pourriez vouloir mapper le fichier en utilisant java.nio.

1

Comme d'autres l'ont dit, tous les tableaux Java de tous les types sont indexés par int, et peuvent donc être de taille max 2 -1 ou 2147483647 éléments (~ 2 milliards). Ceci est spécifié par le Java Language Specification, donc le passage à un autre système d'exploitation ou à une machine virtuelle Java n'aidera pas.

Si vous vouliez écrire une classe pour surmonter cela comme suggéré plus haut que vous pourriez, qui pourrait utiliser un tableau de tableaux (pour beaucoup de flexibilité) ou modifier les types (un long est de 8 octets donc un long[] peut être 8 fois plus grand qu'un byte[]).

2

Vous pouvez envisager d'utiliser FileChannel et MappedByteBuffer à la mémoire carte du fichier,

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

Edit:

Ok, je suis un idiot, il ressemble ByteBuffer ne prend un indice 32 bits ainsi ce qui est étrange puisque le paramètre de taille de FileChannel.map est long ...Mais si vous décidez de diviser le fichier en plusieurs morceaux de 2 Go pour le chargement, je recommanderais quand même des E/S mappées en mémoire, car il peut y avoir de gros avantages en termes de performances. Vous déplacez essentiellement toutes les responsabilités IO vers le noyau OS.

+0

J'ai également atteint la même limite de 'ByteBuffer' qui, je pense, devrait être capable de traiter de longs décalages et index au moins au niveau de l'interface. L'implémentation concrète doit vérifier les plages explicitement. Malheureusement, il n'est pas possible de mapper plus de 2 Go en mémoire. –

+0

Upvote car c'est la bonne façon d'y aller, même si vous devez partitionner les données en morceaux de 2G - envelopper les morceaux dans une classe qui indexe avec un long si vous le souhaitez. –

+0

MappedByteBuffer est également plafonné à 2 Go, pratiquement inutile. Voir http://nyeggen.com/post/2014-05-18-memory-mapping-%3E2gb-of-data-in-java/ pour une solution qui appelle des méthodes JNI internes pour contourner ce problème. – AqD

12
package com.deans.rtl.util; 

import java.io.FileInputStream; 
import java.io.IOException; 

/** 
* 
* @author [email protected] 
* 
* 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<data.length; idx++) { 
      if(fileInputStream.read(data[idx]) != data[idx].length) { 
       throw new IOException("short read"); 
      } 
     } 
    } 
    public long size() { 
     return size; 
    } 
} 
} 
+0

Une bonne idée pour implémenter votre propre ByteArray pour résoudre ce cas. Si ce n'était pas pour votre réponse, je n'aurais probablement pas pensé à le faire. – UnixShadow

+0

Quelqu'un veut-il ajouter une méthode de mise à jour (byte [] b, int start, int size)? :) – rogerdpack

0

java ne supporte pas un tableau direct avec plus de 2^32 éléments à l'heure,

espoir de voir cette fonctionnalité de Java dans le futur

+0

Non, la limite est 2^31 - 1 éléments. Et votre deuxième ligne ne cite aucune référence. – Nayuki

1

Je pense que l'idée de mappage mémoire le fichier (en utilisant le matériel de mémoire virtuelle du processeur) est la bonne approche. Sauf que MappedByteBuffer a la même limitation de 2 Go que les tableaux natifs. Ce gars prétend avoir résolu le problème avec une alternative assez simple à MappedByteBuffer:

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

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

Malheureusement, la machine virtuelle Java se bloque lorsque vous lisez au-delà de 500Mo.

+0

Alors que dans cet exemple spécifique mon cas d'utilisation était de lire un fichier, ce n'est pas le seul cas d'utilisation pour les grands tableaux. –

1

ne limite pas votre auto avec Integer.MAX_VALUE

bien que cette question a été posée il y a plusieurs années, mais ai voulu participer avec un exemple simple en utilisant uniquement java se sans bibliothèques externes

d'abord, disons qu'il est théoriquement impossible, mais pratiquement possible

un nouveau look: si le tableau est un objet d'éléments que d'avoir un objet qui est un tableau de tableaux

est ici l'exemple

import java.lang.reflect.Array; 
import java.util.ArrayList; 
import java.util.List; 

/** 
* 
* @author Anosa 
*/ 
public class BigArray<t>{ 

private final static int ARRAY_LENGTH = 1000000; 

public final long length; 
private List<t[]> arrays; 

public BigArray(long length, Class<t> glasss) 
{ 
    this.length = length; 
    arrays = new ArrayList<>(); 
    setupInnerArrays(glasss); 

} 

private void setupInnerArrays(Class<t> 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]; 
} 

}

et est ici le test

public static void main(String[] args) 
{ 
    long length = 60085147514l; 
    BigArray<String> 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 "); 

} 

ce code est seulement limité par seulement Long.MAX_VALUE et Java tas mais vous pouvez le dépasser comme vous vouloir (je l'ai fait 3800 Mo)

j'espère que c'est utile et fournir une réponse simple

+1

depuis lors j'ai écrit Banana: https://github.com/omry/banana, une lib qui vous permet de faire cela entre autres choses. –

+0

@OmryYadan bon travail que j'ai un coup d'oeil sur quelques exemples bon bro (: - – Anas

+0

Le wiki est assez agréable https://github.com/omry/banana/wiki/Block-Allocators –