【问题标题】:PostgreSQL JDBC Driver logUnclosedConnections is not workingPostgreSQL JDBC 驱动程序 logUnclosedConnections 不工作
【发布时间】:2021-11-04 22:48:15
【问题描述】:

我正在测试 PostgreSQL JDBC 驱动程序的附加属性,以检查它是否有助于找出连接泄漏。即使我特意不关闭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 Wombat@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@Stephen C

的建议,在 System.gc(); 之前添加了 conn = null;

【问题讨论】:

  • 最终这些对象将被垃圾回收,并且 finalize() 方法将被调用,如果调用者忽略了自己这样做,该方法将关闭连接。 我猜在这个没有发生 GC 的简单代码。
  • @ScaryWombat 关于如何完成这项工作的任何想法
  • 循环大约几百次,可能还有一些睡眠时间。
  • 我认为这里发生的情况是,在各个终结器需要运行之前,整个 VM 已被拆除。请注意,您在连接仍在范围内时调用了System.gc(),并分配给了变量conn。因此,它无法被收集——就 JVM 而言,它仍在使用中,而且肯定不是不可访问的。可能在调用 gc 之前将 conn 设置为 null 就足够了。
  • @Gus 按照建议尝试,结果还是一样

标签: java postgresql jdbc


【解决方案1】:

根据 PostgreSQL JDBC 驱动文档:

logUnclosedConnections = boolean

客户端可能会因未能调用其close() 方法而泄漏Connection 对象。最终这些对象将被垃圾回收并调用finalize() 方法,如果调用者自己忽略了这将关闭Connection。使用终结器只是权宜之计。

为了帮助开发人员检测和纠正这些泄漏的来源,添加了logUnclosedConnections URL 参数。它会在每次打开 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);

空分配使Connection 无法访问。 (注意PreparedStatementResultSet 都将引用Connection。我们需要销毁所有可以使Connection 可访问的引用。)

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

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


我在引号中说“工作”有几个原因:

  1. 您不应该在生产代码中做这种事情。调用System.gc() 效率低下。更好的办法是让 GC 在 JVM 认为有必要时运行。

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

【讨论】:

  • 感谢@Stephen C 的回答。正如您所建议的,尝试进行更改,但结果相同。没有关于未关闭连接的报告。
  • 啊……是的。在调用System.gc() 之前将conn 设置为nullconn 变量可能仍然可以访问...直到 main 退出。
  • conn = null; System.gc(); 还是一样
  • null 分配给pstmtrSet。它们将引用连接对象。
  • 请注意,我没有测试这个,因为我不想在我的机器上安装 PostgreSQL ...
猜你喜欢
  • 2013-10-25
  • 2012-12-01
  • 1970-01-01
  • 2011-11-05
  • 2014-06-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-05-06
相关资源
最近更新 更多