How to Properly Close Java Streams
In this tutorial, you'll learn about How to Properly Close Java Streams. We cover key concepts, practical examples, and best practices.
The Problem
Your Java application leaks file handles or sockets. Files.lines(), BufferedReader.lines(), or custom Stream implementations are not closed after use. The OS eventually runs out of file descriptors, causing Too many open files errors.
Quick Fix
Fix 1: Files.lines() Must Be Closed
WRONG — using Files.lines() without closing:
Stream<String> lines = Files.lines(Paths.get("data.csv"));
lines.forEach(System.out::println);
// (file handle is never closed — resource leak)
RIGHT — use try-with-resources:
try (Stream<String> lines = Files.lines(Paths.get("data.csv"))) {
lines.forEach(System.out::println);
} catch (IOException e) {
e.printStackTrace();
}
// (stream and underlying file handle are closed automatically)
Fix 2: BufferedReader.lines() Requires Closing
BufferedReader reader = new BufferedReader(new FileReader("data.txt"));
Stream<String> lines = reader.lines();
// (both reader and stream must be closed)
WRONG — closing only the stream:
lines.close();
// (reader is not closed)
RIGHT — use try-with-resources on the reader:
try (BufferedReader reader = Files.newBufferedReader(Paths.get("data.txt"))) {
reader.lines().forEach(System.out::println);
} catch (IOException e) {
e.printStackTrace();
}
// (reader and stream are both closed)
Fix 3: Infinite Streams (Stream.generate/iterate)
Stream.generate(Math::random)
.forEach(System.out::println);
// (infinite loop — never terminates)
RIGHT — limit the stream:
Stream.generate(Math::random)
.limit(10)
.forEach(System.out::println);
// (terminates after 10 elements)
Fix 4: Custom Stream Implementation
WRONG — implementing Stream without closing underlying resources:
class MyStream<T> implements Stream<T> {
private Connection connection;
// onClose() handler not set — resources not released
}
RIGHT — use onClose to register cleanup:
Stream<T> stream = StreamSupport.stream(spliterator, false)
.onClose(() -> {
connection.close();
file.close();
});
Fix 5: Chained Stream Operations
// Intermediate operations (filter, map) do not open resources.
// Terminal operations (collect, forEach) close the stream.
// But if a terminal operation throws, the stream is not closed:
try {
Files.lines(Paths.get("data.csv"))
.map(line -> { throw new RuntimeException(); })
.collect(Collectors.toList());
} catch (RuntimeException e) {
// The stream is not closed due to the exception!
}
RIGHT — wrap in try-with-resources:
try (Stream<String> lines = Files.lines(Paths.get("data.csv"))) {
List<String> result = lines
.map(line -> { throw new RuntimeException(); })
.collect(Collectors.toList());
} catch (RuntimeException e) {
// stream is closed by try-with-resources
}
Fix 6: Check for Too Many Open Files
# Linux:
lsof -p $(pgrep -f myapp) | wc -l
# (monitor the count over time)
ulimit -n
# 1024 — default file descriptor limit
Increase the limit in the JVM:
java -Xms256m -Xmx1g -jar myapp.jar
# And in the OS:
ulimit -n 65536
Use DodaTech's Resource Monitor to track file descriptor usage, detect stream leaks, and alert on resource exhaustion in Java applications.
Prevention
- Always use try-with-resources for
Files.lines()andBufferedReader.lines(). - Close the underlying reader, not just the stream.
- Register
onClosehandlers for custom streams. - Limit infinite streams with
limit(). - Monitor file descriptor usage in production.
Common Mistakes with stream close
- Non-exhaustive pattern matches that compile with warnings then crash at runtime
- Misunderstanding that
Stringis[Char]with poor performance for large text operations - Using
foldlinstead offoldl'causing stack overflow on large lists
These mistakes appear frequently in real-world JAVA code. DodaTech's contributors have identified these patterns through analysis of open-source projects and production systems.
Practice Exercise
Write a pure function that safely divides two integers using Maybe, then test it with edge cases like division by zero and negative numbers.
This exercise reinforces the concepts covered in this guide. Try implementing it before checking online solutions.
FAQ
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro