#java #postgresql #jdbc
Вопрос:
Я тестирую дополнительные свойства драйвера JDBC PostgreSQL, чтобы проверить, может ли он помочь обнаружить утечки соединения. Несмотря на то, что я намеренно не закрыл соединение, подготовленное заявление, набор результатов, об ошибках не сообщается.
Как я могу заставить 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
добавлено conn = null;
ранее System.gc();
, как было предложено @Gus и @Stephen C
Комментарии:
1. В конечном итоге эти объекты будут собраны в мусор, и будет вызван метод finalize (), который закроет соединение, если вызывающий абонент не сделал этого сам. Я предполагаю, что в этом упрощенном коде не происходит GC.
2. @ScaryWombat Любые мысли о том, как это сделать
3. Повторите несколько сотен раз, возможно, немного поспав.
4. Я думаю, что здесь происходит то, что вся виртуальная машина разрушается до того, как необходимо запустить отдельные завершители. Обратите внимание, что вы вызываете
System.gc()
, когда соединение все еще находится в области действия и назначено переменнойconn
. Таким образом, его невозможно собрать-он все еще используется в JVM и, конечно, не недоступен. Возможно, было бы достаточно просто установитьconn
значение null перед вызовом gc.5. @Гас попробовал, как и предлагалось, но результат тот же
Ответ №1:
В соответствии с документацией драйвера JDBC PostgreSQL:
logUnclosedConnections = boolean
Клиенты могут пропустить
Connection
объекты, не вызвав егоclose()
метод. В конечном итоге эти объекты будут собраны в мусор, иfinalize()
будет вызван метод, который закроетConnection
вызывающий объект, если он не сделал этого сам. Использование финализатора-это всего лишь временное решение.Чтобы помочь разработчикам обнаружить и исправить источник этих утечек
logUnclosedConnections
, был добавлен параметр URL. Он фиксирует трассировку стека при каждомConnection
открытии, и еслиfinalize()
метод достигнут без закрытия, трассировка стека выводится в журнал.
Итак, то, что вы здесь тестируете, — это механизм, предназначенный для отладки неисправного кода, который приводит к утечке Connection
объектов.
Проблема в том, что вы неправильно его тестируете. Как указано в описании, механизм зависит от того, находит ли сборщик мусора недостижимые Connection
объекты. Чтобы это сработало, должны произойти 3 вещи:
- GC нужно запустить.
- ГК нужно найти недостижимое
Connection
. Обратите внимание, что GC не гарантирует, что при каждом запуске он будет находить все недостижимые объекты. - После запуска GC
Connection
необходимо завершить работу с найденными недостижимыми объектами.
Это ваш тест, вы создаете и используете a Connection
, а затем main
завершаете, завершая JVM. К моменту main
окончания GC не запустилась. Таким образом, утечка Connection
не обнаружена.
Чтобы этот тест «работал», вам нужно добавить следующее в конце тела цикла;
rRet = null;
pstmt = null;
Затем добавьте в конце main
метода:
conn = null;
System.gc();
Thread.sleep(1);
Нулевые назначения делают Connection
их недоступными. (Обратите внимание, что PreparedStatement
и ResultSet
будет относиться к Connection
. Нам нужно уничтожить все ссылки, которые могли бы сделать Connection
это доступным.)
В System.gc()
заявлении содержится просьба к JVM запустить GC.
Thread.sleep(1)
Заявление ждет еще секунду … таким образом, JVM получает возможность запустить завершитель соединения до завершения JVM.
Я говорю «работать» в кавычках по нескольким причинам:
- Вы не должны делать такого рода вещи в производственном коде. Звонить
System.gc()
неэффективно. Лучшая идея-просто позволить GC работать, когда JVM сочтет это необходимым. - На самом деле,
System.gc()
не гарантируется вообще ничего делать. Аналогичным образом, вы не можете полагаться на то, что завершители будут запущены немедленно. Таким образом, существуют сценарии, в которых два добавленных оператора не будут работать.
Комментарии:
1. Спасибо за ответ @Stephen C. Как вы и предлагали, попробовал внести изменения, но результат тот же. Нет отчета о незамкнутых соединениях.
2. Ах … да. Установите
conn
значениеnull
перед вызовомSystem.gc()
.conn
Переменная, скорее всего, все еще будет доступна … доmain
выхода.3.
conn = null; System.gc();
все то же4. Назначьте
null
pstmt
иrSet
также. У них будут ссылки на объект подключения.5. Обратите внимание, что я не тестирую это, потому что не хочу устанавливать PostgreSQL на свою машину …