Как читать большой текстовый файл построчно в Java без утечки памяти?
Правильный способ: BufferedReader + FileReader
Для чтения больших текстовых файлов построчно в Java используется связка BufferedReader и FileReader. BufferedReader буферизирует данные (по умолчанию 8 КБ), что значительно ускоряет чтение, а главное — он не загружает весь файл в оперативную память. Строки обрабатываются по одной, поэтому память расходуется только на текущую строку.
Базовый пример (Java 7+ с try-with-resources)
import java.io.*;
public class ReadLargeFile {
public static void main(String[] args) {
String filePath = "bigfile.log";
try (BufferedReader reader = new BufferedReader(new FileReader(filePath))) {
String line;
while ((line = reader.readLine()) != null) {
// Обрабатываем каждую строку
System.out.println("Прочитана строка длиной: " + line.length());
}
} catch (IOException e) {
System.err.println("Ошибка чтения файла: " + e.getMessage());
}
}
}
Что здесь важно:
- try-with-resources — автоматически закрывает reader, даже если возникнет исключение (не нужно писать finally).
- readLine() возвращает
nullпри достижении конца файла — это условие выхода из цикла. - Память: в каждый момент времени в памяти находится только одна строка, а не весь файл.
Улучшенный вариант: указание кодировки
Если файл не в кодировке по умолчанию (например, UTF-8 или Windows-1251), используйте InputStreamReader:
try (BufferedReader reader = new BufferedReader(
new InputStreamReader(new FileInputStream(filePath), "UTF-8"))) {
String line;
while ((line = reader.readLine()) != null) {
// обработка
}
}
Производительность: кастомный буфер
Для очень больших файлов (гигабайты) можно увеличить размер буфера:
int bufferSize = 64 * 1024; // 64 КБ
try (BufferedReader reader = new BufferedReader(
new FileReader(filePath), bufferSize)) {
// ... тот же цикл
}
Альтернатива: Files.lines() (Java 8+)
Современный и лаконичный способ через Stream API. Важно закрывать стрим, иначе ресурсы не освободятся:
import java.nio.file.*;
import java.util.stream.*;
try (Stream<String> lines = Files.lines(Paths.get("bigfile.log"))) {
lines.forEach(line -> {
// обработка строки
System.out.println(line);
});
} catch (IOException e) {
e.printStackTrace();
}
Этот метод тоже читает файл лениво (lazy) — строки подгружаются по мере необходимости. Он автоматически использует UTF-8, если не указано иное.
Чего НЕ стоит делать:
Files.readAllLines()— загружает ВСЕ строки в список в памяти. Для больших файлов — гарантированный OutOfMemoryError.Scannerс большими файлами — медленнее, чем BufferedReader, и тоже может вызвать проблемы с памятью при неправильном использовании.
Итог
Для построчного чтения больших файлов используйте BufferedReader (или Files.lines()). Всегда закрывайте ресурсы (try-with-resources). Не загружайте весь файл в память — обрабатывайте строки по одной.
Альтернативный подход: Apache Commons IO
Если вы не хотите писать цикл вручную, можно использовать библиотеку Apache Commons IO. Она предоставляет готовые утилиты для работы с файлами.
Добавьте зависимость в Maven:
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.15.0</version>
</dependency>
Пример использования:
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.LineIterator;
import java.io.File;
import java.io.IOException;
public class ReadWithCommonsIO {
public static void main(String[] args) {
File file = new File("bigfile.log");
try (LineIterator it = FileUtils.lineIterator(file, "UTF-8")) {
while (it.hasNext()) {
String line = it.nextLine();
// обработка строки
System.out.println("Строка: " + line);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
LineIterator работает аналогично BufferedReader — читает строки по одной, не загружая весь файл. Плюс: код чище, меньше boilerplate. Минус: внешняя зависимость. Если вы уже используете Spring или другой фреймворк с Commons IO — это отличный выбор.
Также можно обработать файл через FileUtils.readLines(), но он возвращает список строк — не подходит для больших файлов.