Spring Boot项目内存溢出排查:org.postgresql.core.Tuple缓存堆积问题
1. Which Library Stores These Tuple Objects?
org.postgresql.core.Tuple is a class from the PostgreSQL JDBC Driver (the official postgresql dependency). These objects represent individual rows fetched from the database during query execution. The driver doesn’t cache Tuples by default—their massive accumulation is almost always due to unclosed database resources (like ResultSet, PreparedStatement, or Connection instances) that are preventing garbage collection.
Common culprits:
- Raw JDBC code missing
close()calls forResultSet/Statement(especially outsidefinallyblocks). - Hibernate
ScrollableResultsorSessioninstances left unclosed, holding references to underlying result sets. - Connection pool leaks where connections with unclosed results are returned to the pool, keeping Tuples in memory indefinitely.
- Custom Spring extensions bypassing
JdbcTemplate’s automatic resource cleanup.
2. Can We Configure the Library to Limit Tuple Storage?
The PostgreSQL JDBC Driver has no built-in cache for Tuples, so there’s no direct setting to cap their count. Instead, fix the root cause and mitigate memory usage with these actionable steps:
Enforce Strict Resource Cleanup
- Raw JDBC: Use try-with-resources to auto-close resources:
try (Connection conn = dataSource.getConnection(); PreparedStatement stmt = conn.prepareStatement("SELECT ..."); ResultSet rs = stmt.executeQuery()) { // Process results here } - Hibernate: Close
ScrollableResultsandSessionexplicitly. For Hibernate 5.2+, use try-with-resources:try (Session session = sessionFactory.openSession()) { ScrollableResults results = session.createNativeQuery("SELECT ...").scroll(); try { // Process results } finally { results.close(); } } - Spring: Rely on
JdbcTemplate/NamedParameterJdbcTemplate—they handle resource cleanup automatically. Avoid manual connection management unless absolutely necessary.
Adjust Query Fetch Size
Limit how many rows the driver loads into memory at once using fetch size. This prevents loading all rows into Tuple objects in a single batch:
- Raw JDBC:
stmt.setFetchSize(1000); // Fetch 1000 rows per batch - Hibernate:
session.createNativeQuery("SELECT ...").setFetchSize(1000).list(); - Spring JdbcTemplate:
jdbcTemplate.query("SELECT ...", rs -> { // Process each row }, stmt -> stmt.setFetchSize(1000));
Validate Connection Pool Configuration
Ensure your connection pool (e.g., HikariCP, Tomcat JDBC) cleans up connections before reuse:
- For HikariCP, enable
connectionTestQuery(e.g.,SELECT 1) or usevalidationTimeoutto detect and discard connections with unclosed resources.
Dig Deeper with Heap Dump Analysis
If the issue persists, take a heap dump (via VisualVM or jmap) and use Eclipse Memory Analyzer (MAT) to trace the reference chain holding the Tuple objects. MAT’s "Path to GC Roots" feature will show exactly which object (e.g., an unclosed ResultSet tied to a leaked Session) is preventing garbage collection.
内容的提问来源于stack exchange,提问作者tabool

