Почему Connection нужно закрывать

Достаточно частая проблема у разработчиков, использующих JDBC - это не закрытый connection в базу данных.

Давайте рассмотрим пример, в котором создается 1000 соединений с БД, внутри каждого соединения выполняется какая-та полезная работа ( в нашем случае создание и удаление таблицы), но никакой обработки завершения работы с БД нет.

Заодно вы сможете заметить, что открытие 1000 соединений с вашей любимой БД - удовольствие достаточно затратное по времени и памяти (попробуйте замерить разницу до выполнения кода и после в качестве упражнения).

Пример кода с github: TODO

    public static void main(String[] args) {
        Logger log = LogManager.getRootLogger();
        try {

            for (int i = 0; i < 1_000; i++) {
                Connection connection = getConnection();
                Statement st = connection.createStatement();

                st.execute("CREATE TABLE IF NOT EXISTS users (id INT NOT NULL AUTO_INCREMENT, name VARCHAR(30), PRIMARY KEY (id))");

                st.execute("DROP TABLE users");
                log.info("Connection # " + i + " was created");
            }

        } catch (SQLException e) {
            e.printStackTrace();
        }

    }

    private static Connection getConnection() throws SQLException {
        return DriverManager.getConnection(URL, USER_NAME, PASSWORD);
    }

Если вы просто запустите этот код и подождете, то по достижении лимита на количество соединений вы получите следующее сообщение:

Если по какой-то причине подобная исключительная ситуация не возникает, попробуйте увеличить количество итераций в цикле или же уменьшить верхний лимит на соединения с БД в настройка самой БД (для MySQL эта настройка называется max_connections и легко меняется через Workbench)

Вся проблема в исчерпании ресурсов: мы сделали какие-то дела, но не обратились к БД с просьбой закрыть двери за гостями.

Так давайте же закроем за собой двери!

Достаточно добавить connection.close() в конце блока кода.

А давайте дадим меньше памяти?

Попробуйте запустить "проблемное" приложение с 10 Мб хипа и вы увидите странную картину: приложение не падает на лимите подключений. Напротив, с минимальным запасом памяти оно умудряется открыть всю тысячу соединений, выполнить работу и спокойно завершиться.

В чем же дело?

Не буду долго вас томить, да вы и сами догадались, что тут замешан сборщик мусора!

Дело в том, что мы объявили переменную connection локально, внутри цикла. Когда мы переходим на новую итерацию цикла, по ссылке становится доступен новый объект, а старый пригоден для сборки мусора.

При таком маленьком хипе сборка происходит очень часто. Специальная подобласть хипа под названием Old быстро наполняется, в Eden/S1/S2 вообще никто долго не задерживается (их размеры 2/1/1 Мб соотвественно), сборки мусора происходят часто, объекты отправляются в мир иной, MySQL отправляет свои коннекты к праотцам.

Никогда так не делайте в проде. Некоторые базы данных, да и старые версии MySQL не удаляют в подобных случаях соединения на свой стороне. Вы легко получите трудноустранимую утечку памяти, решаемую только перезагрузкой сервера.

Урезанная память нас подстраховала, конечно. Но лучше не полагаться на особенности функционирования JVM, а быть предельно корректным при работе с ресурсами, особенно если вы все еще пишите на Java 6.

results matching ""

    No results matching ""