java.lang.OutOfMemoryError: пространство кучи Java Files.readAllBytes (путь) - PullRequest
0 голосов
/ 10 октября 2018

Я преобразовываю большой файл (18 ГБ) в байт [], но я получил эту ошибку:

java.lang.OutOfMemoryError: Java heap space

Этот код отвечает за исключение:

byte[] content = Files.readAllBytes(path); 

Я создаю байтовый массив для отправки его по сети:

createFile(filename.toString(),content);

private ADLStoreClient client; // package com.microsoft.azure.datalake.store
public boolean createFile(String filename, byte[] content) {
        try {
            // create file and write some content
            OutputStream stream = client.createFile(filename, IfExists.OVERWRITE);
            // set file permission
            client.setPermission(filename, "777");
            // append to file
            stream.write(content);
            stream.close();

        } catch (ADLException ex) {
            printExceptionDetails(ex);
            return false;
        } catch (Exception ex) {
            log.error(" Exception: {}", ex);
            return false;
        }
        return true;
    }

Очевидно, readAllBytes () считывает все байты в память и вызывает OutOfMemoryError, я думаю, что это можно решить с помощью потоков, но яя не хорош в них, кто-нибудь может дать правильное решение, спасибо

Ответы [ 4 ]

0 голосов
/ 10 октября 2018

Вот файл класса Stream, который я использую для чтения файлов в поток:

/**
 * Allows a file to be read and iterated over and allow to take advantage of java streams
 * @author locus2k
 *
 */
public class FileStream implements Iterator<byte[]>, Iterable<byte[]>, Spliterator<byte[]> {

  private InputStream stream;
  private int bufferSize;
  private long blockCount;


  /**
   * Create a FileStreamReader
   * @param stream the input stream containing the content to be read
   * @param bufferSize size of the buffer that should be read at once from the stream
   */
  private FileStream(InputStream stream, long fileSize, int bufferSize) {
    this.bufferSize = bufferSize;
    //calculate how many blocks will be generated by this stream
    this.blockCount = (long) Math.ceil((float)fileSize / (float)bufferSize);
    this.stream = stream;
  }

  @Override
  public boolean hasNext() {
    boolean hasNext = false;
    try {
      hasNext = stream.available() > 0;
      return hasNext;
    } catch (IOException e) {
      return false;
    } finally {
      //close the stream if there is no more to read
      if (!hasNext) {
        close();
      }
    }
  }

  @Override
  public byte[] next() {
    try {
      byte[] data = new byte[Math.min(bufferSize, stream.available())];
      stream.read(data);
      return data;
    } catch (IOException e) {
      //Close the stream if next causes an exception
      close();
      throw new RuntimeException(e.getMessage());
    }
  }

  /**
   * Close the stream
   */
  public void close() {
    try {
      stream.close();
    } catch (IOException e) { }
  }

  @Override
  public boolean tryAdvance(Consumer<? super byte[]> action) {
    action.accept(next());
    return hasNext();
  }

  @Override
  public Spliterator<byte[]> trySplit() {
    return this;
  }

  @Override
  public long estimateSize() {
    return blockCount;
  }

  @Override
  public int characteristics() {
    return Spliterator.IMMUTABLE;
  }

  @Override
  public Iterator<byte[]> iterator() {
    return this;
  }

  @Override
  public void forEachRemaining(Consumer<? super byte[]> action) {
    while(hasNext())
      action.accept(next());
  }

  /**
   * Create a java stream
   * @param inParallel if true then the returned stream is a parallel stream; if false the returned stream is a sequential stream.
   * @return stream with the data
   */
  private Stream<byte[]> stream(boolean inParallel) {
    return StreamSupport.stream(this, inParallel);
  }

  /**
   * Create a File Stream reader
   * @param fileName Name of the file to stream
   * @param bufferSize size of the buffer that should be read at once from the stream
   * @return Stream representation of the file
   */
  public static Stream<byte[]> stream(String fileName, int bufferSize) {
    return stream(new File(fileName), bufferSize);
  }

  /**
   * Create a FileStream reader
   * @param file The file to read
   * @param bufferSize the size of each read
   * @return the stream
   */
  public static Stream<byte[]> stream(File file, int bufferSize) {
    try {
      return stream(new FileInputStream(file), bufferSize);
    } catch (FileNotFoundException ex) {
      throw new IllegalArgumentException(ex.getMessage());
    }
  }

  /**
   * Create a file stream reader
   * @param stream the stream to read from (note this process will close the stream)
   * @param bufferSize size of each read
   * @return the stream
   */
  public static Stream<byte[]> stream(InputStream stream, int bufferSize) {
    try {
      return new FileStream(stream, stream.available(), bufferSize).stream(false);
    } catch (IOException ex) {
      throw new IllegalArgumentException(ex.getMessage());
    }
  }

  /**
   * Calculate the number of segments that will be created
   * @param sourceSize the size of the file
   * @param bufferSize the buffer size (or chunk size for each segment to be)
   * @return the number of packets that will be created
   */
  public static long caculateEstimatedSize(long sourceSize, Integer bufferSize) {
    return (long) Math.ceil((float)sourceSize / (float)bufferSize);
  }
}

Затем, чтобы использовать его, вы можете сделать что-то вроде

FileStream.stream("myfile.text", 30000).forEach(b -> System.out.println(b.length));

Это создаст файлstream и каждый вызов forEach будет возвращать байтовый массив, размер буфера, указанный в этом случае, байтовый массив будет 30 000.

0 голосов
/ 10 октября 2018

Как Azure Документация ADLStoreClient сообщает:

createFile(String path, IfExists mode)

создать файл.Если overwriteIfExists имеет значение false и файл уже существует, генерируется исключение.Вызов возвращает ADLFileOutputStream, который затем может быть записан.

Так что-то вроде этого:

try (InputStream in = new FileInputStream(path);
     OutputStream out = client.createFile(filename, IfExists.OVERWRITE)) {
    IOUtils.copyLarge(in, out);
}

Вы можете получить IOUtils из commons-io или сделатьcopyLarge рутируйте себя, это очень просто:

void copyLarge(InputStream in, OutputStream out) throws IOException {
    byte[] buffer = new byte[65536];
    int length;
    while ((length = in.read(buffer)) > 0) {
        out.write(buffer, 0, length);
    }
}
0 голосов
/ 10 октября 2018

Из того, что вы сказали, вы пытаетесь поместить 18 ГБ в память (ОЗУ), чтобы вы могли использовать -Xmsn и установить его на 18 ГБ, но вам понадобится 18 ГБ свободной памяти, о которой вы можете прочитать в java-документах: -Xmsn Java

0 голосов
/ 10 октября 2018

Как то так?(если вам нравится обрабатывать его построчно)

try (Stream<String> stream = Files.lines(Paths.get(fileName))) {

            stream.forEach(System.out::println);

        } catch (IOException e) {
            e.printStackTrace();
        }
...
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...