Resource management is a critical yet often overlooked aspect of Java development. This article explores how to properly manage resources in Java to prevent leaks, using a real example from the SeaTunnel project.
A Fix in SeaTunnel
In Apache SeaTunnel, the HiveSink component once had a typical resource leak issue. The code comparison before and after the fix is shown below:
Before the fix:
@Override
public List<FileAggregatedCommitInfo> commit(...) throws IOException {
    HiveMetaStoreProxy hiveMetaStore = HiveMetaStoreProxy.getInstance(pluginConfig);
    List<FileAggregatedCommitInfo> errorCommitInfos = super.commit(aggregatedCommitInfos);
    if (errorCommitInfos.isEmpty()) {
        // Handle partition logic...
    }
    hiveMetaStore.close();  // Will not execute if an exception occurs above
    return errorCommitInfos;
}
After the fix:
@Override
public List<FileAggregatedCommitInfo> commit(...) throws IOException {
    List<FileAggregatedCommitInfo> errorCommitInfos = super.commit(aggregatedCommitInfos);
    HiveMetaStoreProxy hiveMetaStore = HiveMetaStoreProxy.getInstance(pluginConfig);
    try {
        if (errorCommitInfos.isEmpty()) {
            // Handle partition logic...
        }
    } finally {
        hiveMetaStore.close();  // Ensures the resource is always released
    }
    return errorCommitInfos;
}
Though the change appears small, it effectively prevents resource leaks and ensures system stability. Let’s now explore general strategies for Java resource management.
What Is a Resource Leak?
A resource leak occurs when a program acquires a resource but fails to properly release it, resulting in long-term resource occupation. Common resources that require proper management include:
- 📁 File handles
- 📊 Database connections
- 🌐 Network connections
- 🧵 Thread resources
- 🔒 Lock resources
- 💾 Memory resources
Improper resource management may lead to:
- Performance degradation
- Out of memory errors
- Application crashes
- Service unavailability
Two Approaches to Resource Management
(1) Traditional Approach: try-catch-finally
Connection conn = null;
try {
    conn = DriverManager.getConnection(url, user, password);
    // Use connection
} catch (SQLException e) {
    // Exception handling
} finally {
    if (conn != null) {
        try {
            conn.close();
        } catch (SQLException e) {
            // Handle close exception
        }
    }
}
Drawbacks:
- Verbose code
- Complex nesting
- Easy to forget closing resources
- Harder to maintain when multiple resources are involved
(2) Modern Approach: try-with-resources (Java 7+)
try (Connection conn = DriverManager.getConnection(url, user, password)) {
    // Use connection
} catch (SQLException e) {
    // Exception handling
}
Advantages:
- Cleaner and more concise
- Automatically closes resources
- Handles exceptions gracefully
- Maintains readability even with multiple resources
Custom Resource Classes
To manage custom resources, implement the AutoCloseable interface:
public class MyResource implements AutoCloseable {
    private final ExpensiveResource resource;
    public MyResource() {
        this.resource = acquireExpensiveResource();
    }
    @Override
    public void close() {
        if (resource != null) {
            try {
                resource.release();
            } catch (Exception e) {
                logger.error("Error while closing resource", e);
            }
        }
    }
}
Key principles:
- The close()method should be idempotent
- Handle internal exceptions within close()
- Log failures during resource release
Common Pitfalls and Solutions
(1) Resource management inside loops
Incorrect:
public void processFiles(List<String> filePaths) throws IOException {
    for (String path : filePaths) {
        FileInputStream fis = new FileInputStream(path); // Potential leak
        // Process file
        fis.close(); // Won’t run if an exception is thrown
    }
}
Correct:
public void processFiles(List<String> filePaths) throws IOException {
    for (String path : filePaths) {
        try (FileInputStream fis = new FileInputStream(path)) {
            // Process file
        } // Automatically closed
    }
}
(2) Nested resource handling
// Recommended approach
public void nestedResources() throws Exception {
    try (
        OutputStream os = new FileOutputStream("file.txt");
        BufferedOutputStream bos = new BufferedOutputStream(os)
    ) {
        // Use bos
    } // Automatically closed in reverse order
}
Real-World Examples
(1) Database Connection
public void processData() throws SQLException {
    try (
        Connection conn = getConnection();
        Statement stmt = conn.createStatement();
        ResultSet rs = stmt.executeQuery("SELECT * FROM users")
    ) {
        while (rs.next()) {
            // Process data
        }
    } // All resources closed automatically
}
(2) File Copy
public void copyFile(String source, String target) throws IOException {
    try (
        FileInputStream in = new FileInputStream(source);
        FileOutputStream out = new FileOutputStream(target)
    ) {
        byte[] buffer = new byte[1024];
        int len;
        while ((len = in.read(buffer)) > 0) {
            out.write(buffer, 0, len);
        }
    }
}
(3) HTTP Request
public String fetchData(String urlString) throws IOException {
    URL url = new URL(urlString);
    HttpURLConnection connection = (HttpURLConnection) url.openConnection();
    connection.setRequestMethod("GET");
    try (BufferedReader reader = new BufferedReader(
            new InputStreamReader(connection.getInputStream()))) {
        StringBuilder response = new StringBuilder();
        String line;
        while ((line = reader.readLine()) != null) {
            response.append(line);
        }
        return response.toString();
    } finally {
        connection.disconnect();
    }
}
Summary & Best Practices
- Prefer try-with-resources for managing resources
- If try-with-resources is not applicable, use finallyblocks to release resources
- Custom resource classes should implement AutoCloseable
- Release resources in reverse order of acquisition
- Handle exceptions in close()gracefully
- Be extra cautious with resource creation inside loops
 

 
    
Top comments (0)