PostgreSQL JDBC驱动程序loguncloseconnections不工作



我正在测试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。使用终结器只是一个权宜之计。

为了帮助开发人员检测和纠正这些泄漏的来源,logUnclosedConnectionsURL参数已经添加。它在每个Connection打开时捕获堆栈跟踪,如果到达finalize()方法而没有关闭,则将堆栈跟踪打印到日志中。

所以,你在这里测试的是一种机制,用于调试泄漏Connection对象的错误代码。

问题是你没有以正确的方式测试它。如描述所述,机制依赖于垃圾收集器查找不可达的Connection对象。要做到这一点,需要满足3个条件:

  1. GC需要运行
  2. GC需要找到不可达的Connection。注意,GC不能保证每次运行时都能找到所有不可达的对象。
  3. GC运行后,它发现的不可达的Connection对象需要最终确定。

在您的测试中,您创建并使用Connection,然后main结束,终止JVM。到main结束时,GC还没有运行。因此没有检测到泄漏的Connection

要使此测试"有效"您需要在循环体的末尾添加以下内容;

rRet = null;
pstmt = null;

然后在main方法的末尾添加:

conn = null;
System.gc();
Thread.sleep(1);

null赋值使得Connection不可达。(注意,PreparedStatementResultSet都指向Connection。我们需要销毁所有可能使Connection可访问的引用)

System.gc()语句请求JVM运行GC

Thread.sleep(1)语句再等待一秒钟…以便JVM有机会在JVM结束之前运行连接终结器。


我说"work"在引号中有几个原因:

  1. 你不应该在产品代码中做这种事情。调用System.gc()是低效的。一个更好的主意是,当JVM认为有必要时,只让GC运行。

  2. 事实上,System.gc()根本不能保证做任何事情。同样,您不能依赖于立即运行的终结器。因此,在某些情况下,这两个添加的语句将不起作用。