使用try-with-resources和HikariCP连接泄漏
作者:互联网
以下代码触发连接泄漏警告.我使用的是OpenJDK 1.7.0_80和HikariCP 2.2.5(也可以使用最新的HikariCP 2.3.9重现).我错过了什么吗?
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import org.junit.Test;
import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;
public class HikaryAutoCloseTest {
private static HikariDataSource configureDataSource() {
try {
Class.forName("org.postgresql.Driver");
} catch (ClassNotFoundException e) {
throw new RuntimeException(e);
}
HikariConfig config = new HikariConfig();
config.setJdbcUrl("jdbc:postgresql://127.0.0.1/DATABASE");
config.setUsername("USERNAME");
config.setPassword("PASSWORD");
config.setLeakDetectionThreshold(10000);
config.addDataSourceProperty("cachePrepStmts", "true");
config.addDataSourceProperty("useServerPrepStmts", "true");
return new HikariDataSource(config);
}
@Test
public void testHikaryAutoClose() {
HikariDataSource dataSource = configureDataSource();
boolean ret = shouldNotLeakConnection(dataSource);
if (ret) {
System.out.println("UPDATE okey");
}
/* Wait for LeakTask to complain */
try {
Thread.sleep(20000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println("Exiting");
}
private boolean shouldNotLeakConnection(HikariDataSource dataSource) {
String sql = "INSERT INTO error_logs (description) values (?)";
try (Connection conn = dataSource.getConnection(); PreparedStatement stmt = conn.prepareStatement(sql);) {
stmt.setString(1, "description");
return stmt.executeUpdate() != 0; // minor changes to this line remove the leak
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
}
更新:略微更改return语句消除了泄漏:
private boolean shouldNotLeakConnection(HikariDataSource dataSource) {
String sql = "INSERT INTO error_logs (description) values (?)";
try (Connection conn = dataSource.getConnection(); PreparedStatement stmt = conn.prepareStatement(sql);) {
stmt.setString(1, "description");
boolean ret = stmt.executeUpdate() != 0;
return ret;
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
解决方法:
似乎问题来自于AspectJ 1.7.2中的一个错误.它产生了以下字节码:
private boolean shouldNotLeakConnection(com.zaxxer.hikari.HikariDataSource);
Code:
0: ldc #115 // String INSERT INTO error_logs (description) values (?)
2: astore_2
3: aconst_null
4: astore_3
5: aconst_null
6: astore 4
8: aload_1
9: invokevirtual #117 // Method com/zaxxer/hikari/HikariDataSource.getConnection:()Ljava/sql/Connection;
12: astore 5
14: aload 5
16: aload_2
17: invokeinterface #121, 2 // InterfaceMethod java/sql/Connection.prepareStatement:(Ljava/lang/String;)Ljava/sql/PreparedStatement;
22: astore 6
24: aload 6
26: iconst_1
27: ldc #127 // String description
29: invokeinterface #129, 3 // InterfaceMethod java/sql/PreparedStatement.setString:(ILjava/lang/String;)V
34: aload 6
36: invokeinterface #135, 1 // InterfaceMethod java/sql/PreparedStatement.executeUpdate:()I
41: ifeq 46
44: iconst_1
45: ireturn
46: iconst_0
47: aload 6
49: ifnull 59
52: aload 6
54: invokeinterface #139, 1 // InterfaceMethod java/sql/PreparedStatement.close:()V
59: aload 5
61: ifnull 71
64: aload 5
66: invokeinterface #142, 1 // InterfaceMethod java/sql/Connection.close:()V
71: ireturn
72: astore_3
73: aload 6
75: ifnull 85
78: aload 6
80: invokeinterface #139, 1 // InterfaceMethod java/sql/PreparedStatement.close:()V
85: aload_3
86: athrow
87: astore 4
89: aload_3
90: ifnonnull 99
93: aload 4
95: astore_3
96: goto 111
99: aload_3
100: aload 4
102: if_acmpeq 111
105: aload_3
106: aload 4
108: invokevirtual #143 // Method java/lang/Throwable.addSuppressed:(Ljava/lang/Throwable;)V
111: aload 5
113: ifnull 123
116: aload 5
118: invokeinterface #142, 1 // InterfaceMethod java/sql/Connection.close:()V
123: aload_3
124: athrow
125: astore 4
127: aload_3
128: ifnonnull 137
131: aload 4
133: astore_3
134: goto 149
137: aload_3
138: aload 4
140: if_acmpeq 149
143: aload_3
144: aload 4
146: invokevirtual #143 // Method java/lang/Throwable.addSuppressed:(Ljava/lang/Throwable;)V
149: aload_3
150: athrow
151: astore_3
152: new #25 // class java/lang/RuntimeException
155: dup
156: aload_3
157: invokespecial #27 // Method java/lang/RuntimeException."<init>":(Ljava/lang/Throwable;)V
160: athrow
Exception table:
from to target type
24 47 72 any
71 72 72 any
14 59 87 any
71 87 87 any
8 125 125 any
3 71 151 Class java/sql/SQLException
72 151 151 Class java/sql/SQLException
注意第45行,ireturn跳过两个close()方法.
使用AspectJ 1.8.6,生成正确的字节码:
private boolean shouldNotLeakConnection(com.zaxxer.hikari.HikariDataSource);
Code:
0: ldc #115 // String INSERT INTO error_logs (description) values (?)
2: astore_2
3: aconst_null
4: astore_3
5: aconst_null
6: astore 4
8: aload_1
9: invokevirtual #117 // Method com/zaxxer/hikari/HikariDataSource.getConnection:()Ljava/sql/Connection;
12: astore 5
14: aload 5
16: aload_2
17: invokeinterface #121, 2 // InterfaceMethod java/sql/Connection.prepareStatement:(Ljava/lang/String;)Ljava/sql/PreparedStatement;
22: astore 6
24: aload 6
26: iconst_1
27: ldc #127 // String description
29: invokeinterface #129, 3 // InterfaceMethod java/sql/PreparedStatement.setString:(ILjava/lang/String;)V
34: aload 6
36: invokeinterface #135, 1 // InterfaceMethod java/sql/PreparedStatement.executeUpdate:()I
41: ifeq 48
44: iconst_1
45: goto 49
48: iconst_0
49: aload 6
51: ifnull 61
54: aload 6
56: invokeinterface #139, 1 // InterfaceMethod java/sql/PreparedStatement.close:()V
61: aload 5
63: ifnull 73
66: aload 5
68: invokeinterface #142, 1 // InterfaceMethod java/sql/Connection.close:()V
73: ireturn
74: astore_3
75: aload 6
77: ifnull 87
80: aload 6
82: invokeinterface #139, 1 // InterfaceMethod java/sql/PreparedStatement.close:()V
87: aload_3
88: athrow
89: astore 4
91: aload_3
92: ifnonnull 101
95: aload 4
97: astore_3
98: goto 113
101: aload_3
102: aload 4
104: if_acmpeq 113
107: aload_3
108: aload 4
110: invokevirtual #143 // Method java/lang/Throwable.addSuppressed:(Ljava/lang/Throwable;)V
113: aload 5
115: ifnull 125
118: aload 5
120: invokeinterface #142, 1 // InterfaceMethod java/sql/Connection.close:()V
125: aload_3
126: athrow
127: astore 4
129: aload_3
130: ifnonnull 139
133: aload 4
135: astore_3
136: goto 151
139: aload_3
140: aload 4
142: if_acmpeq 151
145: aload_3
146: aload 4
148: invokevirtual #143 // Method java/lang/Throwable.addSuppressed:(Ljava/lang/Throwable;)V
151: aload_3
152: athrow
153: astore_3
154: new #25 // class java/lang/RuntimeException
157: dup
158: aload_3
159: invokespecial #27 // Method java/lang/RuntimeException."<init>":(Ljava/lang/Throwable;)V
162: athrow
Exception table:
from to target type
24 49 74 any
73 74 74 any
14 61 89 any
73 89 89 any
8 127 127 any
3 73 153 Class java/sql/SQLException
74 153 153 Class java/sql/SQLException
请注意,在第45行,ireturn被替换为不跳过两个close()方法的goto.
谢谢@brettw帮助我缩小问题的范围.
标签:java,openjdk,try-with-resources,hikaricp 来源: https://codeday.me/bug/20190623/1273179.html