我正在测试PostgreSQL JDBC Driver的附加属性,以检查它是否有助于发现连接泄漏。即使我故意没有关闭Connection, PreparedStatement, ResultSet,也没有报告错误。
如何使logUnclosedConnections
工作
这是我的示例程序
public static void main( String[] args ) throws Exception {
Properties dbProps = new Properties();
dbProps.setProperty( "user", "xxxxxxx" );
dbProps.setProperty( "password", "xxxxxxxxxx" );
dbProps.setProperty( "logServerErrorDetail", "true" );
dbProps.setProperty( "logUnclosedConnections", "true" );
dbProps.setProperty( "loggerLevel", "DEBUG" );
Connection conn = DriverManager.getConnection( "jdbc:postgresql://xxxxx.xxxxx.xxx:xxx/xxxxx", dbProps );
try {
PreparedStatement pstmt = conn.prepareStatement( "select count(*) from xx.xx limit 10" );
ResultSet rSet = pstmt.executeQuery();
while ( rSet.next() ) {
System.out.println( "count is " + rSet.getInt( 1 ) );
}
} catch ( Exception e ) {
e.printStackTrace();
}
}
结果
count is 687
Sep 08, 2021 12:21:44 PM org.postgresql.Driver connect
FINE: Connecting with URL: jdbc:postgresql://xxxxx.xxxx.xxxx:xxx/xxxx
Sep 08, 2021 12:21:44 PM org.postgresql.jdbc.PgConnection <init>
FINE: PostgreSQL JDBC Driver 42.2.23
Sep 08, 2021 12:21:44 PM org.postgresql.jdbc.PgConnection setDefaultFetchSize
FINE: setDefaultFetchSize = 0
Sep 08, 2021 12:21:44 PM org.postgresql.jdbc.PgConnection setPrepareThreshold
FINE: setPrepareThreshold = 5
Sep 08, 2021 12:21:44 PM org.postgresql.core.v3.ConnectionFactoryImpl openConnectionImpl
FINE: Trying to establish a protocol version 3 connection to xxxx.xxxx.xxx:xxx
Sep 08, 2021 12:21:44 PM org.postgresql.core.v3.ConnectionFactoryImpl tryConnect
FINE: Receive Buffer Size is 65,536
Sep 08, 2021 12:21:44 PM org.postgresql.core.v3.ConnectionFactoryImpl tryConnect
FINE: Send Buffer Size is 65,536
Sep 08, 2021 12:21:44 PM org.postgresql.ssl.MakeSSL convert
FINE: converting regular socket connection to ssl
编辑1
@Scary Wombatand@Stephen C,但结果是一样的。
未关闭连接未报告.
public static void main( String[] args ) throws Exception {
Properties dbProps = new Properties();
dbProps.setProperty( "user", "postgres" );
dbProps.setProperty( "password", "AuR0ra$dba#$" );
dbProps.setProperty( "logServerErrorDetail", "true" );
dbProps.setProperty( "logUnclosedConnections", "true" );
dbProps.setProperty( "loggerLevel", "DEBUG" );
Connection conn = DriverManager.getConnection( "jdbc:postgresql://devdbmaster.koncert.com:5499/dev_koncert_v10.5", dbProps );
try {
for ( int i = 0; i < 100; i++ ) {
PreparedStatement pstmt = conn.prepareStatement( "select count(*) from cl.users limit 10" );
ResultSet rSet = pstmt.executeQuery();
while ( rSet.next() ) {
System.out.println( "count is " + rSet.getInt( 1 ) );
}
Thread.sleep( 10 );
}
} catch ( Exception e ) {
e.printStackTrace();
}
conn = null;
System.gc();
Thread.sleep( 1 );
}
编辑2
按照@Gus的建议,在System.gc();
之前添加conn = null;
and@Stephen C
根据PostgreSQL JDBC驱动程序文档:
logUnclosedConnections = boolean
客户端可能由于未能调用
close()
方法而泄漏Connection
对象。最终,这些对象将被垃圾收集,finalize()
方法将被调用,如果调用者忽略了自己这样做,它将关闭Connection
。使用终结器只是一个权宜之计。为了帮助开发人员检测和纠正这些泄漏的来源,
logUnclosedConnections
URL参数已经添加。它在每个Connection
打开时捕获堆栈跟踪,如果到达finalize()
方法而没有关闭,则将堆栈跟踪打印到日志中。
所以,你在这里测试的是一种机制,用于调试泄漏Connection
对象的错误代码。
问题是你没有以正确的方式测试它。如描述所述,机制依赖于垃圾收集器查找不可达的Connection
对象。要做到这一点,需要满足3个条件:
- GC需要运行
- GC需要找到不可达的
Connection
。注意,GC不能保证每次运行时都能找到所有不可达的对象。 - GC运行后,它发现的不可达的
Connection
对象需要最终确定。
在您的测试中,您创建并使用Connection
,然后main
结束,终止JVM。到main
结束时,GC还没有运行。因此没有检测到泄漏的Connection
。
要使此测试"有效"您需要在循环体的末尾添加以下内容;
rRet = null;
pstmt = null;
然后在main
方法的末尾添加:
conn = null;
System.gc();
Thread.sleep(1);
null赋值使得Connection
不可达。(注意,PreparedStatement
和ResultSet
都指向Connection
。我们需要销毁所有可能使Connection
可访问的引用)
System.gc()
语句请求JVM运行GC
Thread.sleep(1)
语句再等待一秒钟…以便JVM有机会在JVM结束之前运行连接终结器。
我说"work"在引号中有几个原因:
你不应该在产品代码中做这种事情。调用
System.gc()
是低效的。一个更好的主意是,当JVM认为有必要时,只让GC运行。事实上,
System.gc()
根本不能保证做任何事情。同样,您不能依赖于立即运行的终结器。因此,在某些情况下,这两个添加的语句将不起作用。