Как бороться с большими строками и ограниченной памятью - PullRequest
6 голосов
/ 27 января 2010

У меня есть файл, из которого я читаю данные. Весь текст из этого файла хранится в строковой переменной (очень большая переменная). Затем в другой части моего приложения я хочу пройтись по этой строке и получить полезную информацию, шаг за шагом (анализ строки).

Тем временем моя память заполняется, а исключение OutOfMemory не позволяет мне обрабатывать дальше Я думаю, что было бы лучше обрабатывать данные непосредственно при чтении входного потока из файла. Но для организационных целей я хотел бы передать строку в другую часть моего приложения.

Что нужно сделать, чтобы не переполнить память?

Ответы [ 4 ]

6 голосов
/ 27 января 2010

Если вы можете немного ослабить свои требования, вы можете реализовать java.lang.CharSequence , подкрепленную вашим файлом.

Поддерживается CharSequence во многих местах в JDK (String - это CharSequence). Так что это хорошая альтернатива реализации на основе Reader.

6 голосов
/ 27 января 2010

Вы должны использовать BufferedInputReader вместо хранения всего этого в одну большую строку.

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

4 голосов
/ 28 января 2010

Другие предложили прочитать и обработать части вашего файла за один раз. Если возможно, один из этих способов будет лучше.

Однако, если это невозможно, и вы можете изначально загрузить String в память, как вы укажете, но более поздний анализ этой строки создает проблемы, вы можете использовать подстроки. В Java подстрока отображается поверх исходного массива char и просто занимает память для базового Object, а затем для указателей на начало и длину int.

Итак, когда вы найдете часть строки, которую хотите сохранить отдельно, используйте что-то вроде:

String piece = largeString.substring(foundStart, foundEnd);

Если вы вместо этого или кода, который внутренне это делает, то использование памяти резко возрастет:

new String(largeString.substring(foundStart, foundEnd));

Обратите внимание, что именно по этой причине вы должны использовать String.substring() с осторожностью. У вас может быть очень большая строка, из которой вы берете подстроку и затем отбрасываете свою ссылку на исходную строку. Проблема в том, что подстрока все еще ссылается на исходный большой массив char. GC не освободит это, пока подстрока также не удалена. В подобных случаях полезно на самом деле использовать new String(...), чтобы гарантировать, что неиспользуемый большой массив будет отброшен GC (это один из немногих случаев, когда вам следует использовать new String(...)).

Другой метод, если вы ожидаете, что вокруг будет много маленьких строк и, вероятно, они будут иметь одинаковые значения, но исходят из внешнего источника (например, файла), - это использовать .intern() после создания новой строки.

Примечание: это зависит от реализации String, о которой вам действительно не нужно знать, но на практике для больших приложений иногда приходится полагаться на эти знания. Имейте в виду, что будущие версии Java могут изменить это (хотя маловероятно).

1 голос
/ 27 января 2010

Вы должны пересмотреть свой алгоритм работы с большими данными. Вы должны обрабатывать эти данные по частям или использовать произвольный доступ к файлам без сохранения данных в памяти. Например, вы можете использовать StringTokenizer или StreamTokenizer, как сказано @Zombies. Вы можете увидеть методы parser-lexer: когда синтаксический анализатор анализирует некоторое выражение, он просит lexer прочитать следующий лексем (токены), но не читает весь входной поток сразу.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...