Nano Hash - криптовалюты, майнинг, программирование

java.lang.OutOfMemoryError при чтении файла в массив byte[]

Есть ли более чистый и быстрый способ сделать это:

BufferedReader inputReader = new BufferedReader(new InputStreamReader(context.openFileInput("data.txt")));
String inputString;
StringBuilder stringBuffer = new StringBuilder();
while ((inputString = inputReader.readLine()) != null) {
    stringBuffer.append(inputString + "\n");
}
text = stringBuffer.toString();
byte[] data = text.getBytes();

В основном я пытаюсь преобразовать файл в byte[], за исключением того, что если файл достаточно большой, я сталкиваюсь с ошибкой нехватки памяти. Я искал SO для решения , Я пытался сделать это здесь, и это не сработало. Любая помощь будет оценена по достоинству.


  • В этой теме много хороших мыслей по этому поводу. 30.01.2013
  • Я попытался реализовать фактический ответ, единственная проблема в том, что мне делать с mbb? Вроде это уже в байте []? 30.01.2013
  • Присвоение имени StringBuilder stringBuffer сбивает с толку, поскольку StringBuffer — это потокобезопасная версия StringBuilder. Просто говорю. 30.01.2013

Ответы:


1

Несколько предложений:

  1. Вам не нужно создавать построитель строк. Вы можете напрямую читать байты из файла.
  2. Если вы читаете несколько файлов, проверьте, остались ли в памяти массивы byte[], даже если они не требуются.
  3. Наконец, увеличьте максимальный объем памяти для вашего Java-процесса, используя параметр -Xmx.
30.01.2013
  • Большое спасибо, а также за совет, приведенный ниже, в основном загрузили файл, затем очистили старый и продолжайте делать это, чтобы он не стал чрезмерно большим. 31.01.2013
  • 3 плохой совет. Есть случаи, когда люди увеличивают максимальный размер кучи, что на самом деле вызывает ошибку OutOfMemoryError. Вот хороший пример. Я также видел, как это происходит с Oracle JDK. . Я думаю, это связано с тем, что при увеличении максимального размера кучи с помощью -Xmx вы также уменьшаете доступную собственную память, а FileInputStream использует собственную память, хотя это всего лишь теория. Флаг -Xmx увеличивает только максимальный размер кучи, а не максимальный объем памяти для вашего java-процесса, как вы указываете, который ограничен 4 ГБ для 32-разрядной Java. 11.10.2018

  • 2

    Поскольку мы знаем размер этого файла, примерно половину памяти можно сэкономить, выделив массив байтов заданного размера напрямую, а не расширив его:

    byte [] data = new byte[ (int) file.length() ];
    FileInputStream fin = new FileInputStream(file);
    int n = 0;
    while ( (n = fin.read(data, n, data.length() - n) ) > 0);
    

    Это позволит избежать выделения ненужных дополнительных структур. Массив байтов выделяется только один раз и имеет правильный размер с самого начала. Цикл while обеспечивает загрузку всех данных ( read(byte[], offset, length) может читать только часть файла, но возвращает количество прочитанных байтов).

    Пояснение: когда StringBuilder заканчивается, он выделяет новый буфер, который в два раза больше исходного буфера. На данный момент мы используем примерно в два раза больше памяти, чем было бы минимально необходимо. В самом вырожденном случае (один последний байт не помещается в какой-то и без того большой буфер) может потребоваться почти в три раза больше минимального объема оперативной памяти.

    30.01.2013
  • Когда StringBuilder заканчивается, он выделяет новый буфер. На данный момент у нас есть два буфера, старый и новый. Следовательно, на данный момент мы используем в два раза больше памяти, чем требуется минимально. 30.01.2013
  • Я также думал, что новый буфер будет в два раза больше старого (он точно больше, верно? :p). Документация не была ясной по этому поводу. 30.01.2013
  • Да, это, вероятно, будет (old_size + 1) * 2, что можно проверить в исходный код OpenJDK. Следовательно, в самых вырожденных крайних случаях может потребоваться почти в три раза больше памяти, чем необходимо. 30.01.2013
  • Это то, о чем я думал. Это помогает продвигать ваше решение;) Спасибо за ссылку. 30.01.2013
  • Что делать, если размер файла не подходит для int? Что делать, если массив все еще слишком велик для памяти? Не ответ. 30.01.2013
  • Задача состоит в том, чтобы прочитать файл в массив, чтобы сам массив можно было определить и поместить в память. 30.01.2013
  • Я пытался реализовать это решение, которое в настоящее время тестируется. Я думаю, вы хотели сказать fin.read вместо file.read. Спасибо за помощь. 31.01.2013
  • Спасибо, это, кажется, работает довольно хорошо. Я продолжу тестировать его, хотя единственное, с чем я мог столкнуться, - это проблема с чтением только части файла, но я посмотрю, является ли это сбоем в моем другом коде. 05.02.2013

  • 3

    Если у вас недостаточно памяти для хранения всего файла, вы можете попробовать переосмыслить свой алгоритм для обработки данных файла во время его чтения, не создавая большой массив данных byte[].

    Если вы уже пробовали увеличить java память, играя с параметром -Xmx, то нет никакого решения, которое позволит вам хранить в памяти данные, которые не могут быть там размещены из-за ее большого размера.

    30.01.2013

    4

    Это похоже на File to byte[] в Java

    В настоящее время вы читаете байты, конвертируете их в символы, а затем пытаетесь превратить их обратно в байты. Из класса InputStreamReader в Java API:

    InputStreamReader — это мост от потоков байтов к потокам символов: он считывает байты и декодирует их в символы.

    Было бы намного эффективнее просто читать в байтах.

    Один из способов — использовать ByteArrayInputStream непосредственно на context.openFileInput() или Jakarta Commons IOUtils.toByteArray(InputStream), или, если вы используете JDK7, вы можете использовать Files.readAllBytes(Path).

    30.01.2013

    5

    «Чище и быстрее» — вообще не делать этого. Это не масштабируется. Обрабатывайте файл по частям.

    30.01.2013

    6

    Вы копируете байты в char (которые используют вдвое больше места) и снова обратно в байты.

    InputStream in = context.openFileInput("data.txt");
    ByteArrayOutputStream bais = new ByteArrayOutputStream();
    byte[] bytes = new byte[8192];
    for(int len; (lne = in.read(bytes) > 0;)
       bais.write(bytes, 0, len);
    in.close();
    return bais.toByteArray();
    

    Это вдвое уменьшит потребность в памяти, но все равно может означать, что вам не хватает памяти. В этом случае вы должны либо

    • увеличить максимальный размер кучи
    • обрабатывать файл постепенно, а не все сразу
    • используйте файлы с отображением памяти, которые позволяют вам «загружать» файл без использования большого количества кучи.
    30.01.2013
  • Вы имеете в виду ByteArrayOutputStream, но это все равно не решает проблему. 30.01.2013
  • @EJP Правильно. Возможно, вы пропустили ... but it can still mean you run out of memory. In this case you have to either ... 30.01.2013

  • 7

    Это решение будет проверять свободную память перед загрузкой...

    File test = new File("c:/tmp/example.txt");
    
        long freeMemory = Runtime.getRuntime().freeMemory();
        if(test.length()<freeMemory) {
            byte[] bytes = new byte[(int) test.length()];
            FileChannel fc = new FileInputStream(test).getChannel();
            MappedByteBuffer mbb = fc.map(FileChannel.MapMode.READ_ONLY, 0, (int) fc.size());
    
            while(mbb.hasRemaining()) {
                mbb.get(bytes);
            }
            fc.close();
        }
    
    30.01.2013
  • А также? Что он делает, если не хватает памяти? Не ответ. 30.01.2013
  • Если памяти недостаточно, то это невыполнимо, требование, как указано в вопросе, состоит в том, чтобы иметь массив байтов, содержащий все содержимое файла! Да, я согласен с вашим сообщением, если это возможно, следует транслировать и обрабатывать кусками. 30.01.2013
  • Новые материалы

    Кластеризация: более глубокий взгляд
    Кластеризация — это метод обучения без учителя, в котором мы пытаемся найти группы в наборе данных на основе некоторых известных или неизвестных свойств, которые могут существовать. Независимо от..

    Как написать эффективное резюме
    Предложения по дизайну и макету, чтобы представить себя профессионально Вам не позвонили на собеседование после того, как вы несколько раз подали заявку на работу своей мечты? У вас может..

    Частный метод Python: улучшение инкапсуляции и безопасности
    Введение Python — универсальный и мощный язык программирования, известный своей простотой и удобством использования. Одной из ключевых особенностей, отличающих Python от других языков, является..

    Как я автоматизирую тестирование с помощью Jest
    Шутка для победы, когда дело касается автоматизации тестирования Одной очень важной частью разработки программного обеспечения является автоматизация тестирования, поскольку она создает..

    Работа с векторными символическими архитектурами, часть 4 (искусственный интеллект)
    Hyperseed: неконтролируемое обучение с векторными символическими архитектурами (arXiv) Автор: Евгений Осипов , Сачин Кахавала , Диланта Хапутантри , Тимал Кемпития , Дасвин Де Сильва ,..

    Понимание расстояния Вассерштейна: мощная метрика в машинном обучении
    В обширной области машинного обучения часто возникает необходимость сравнивать и измерять различия между распределениями вероятностей. Традиционные метрики расстояния, такие как евклидово..

    Обеспечение масштабируемости LLM: облачный анализ с помощью AWS Fargate и Copilot
    В динамичной области искусственного интеллекта все большее распространение получают модели больших языков (LLM). Они жизненно важны для различных приложений, таких как интеллектуальные..