HikariCP - HikariDataSource的创建


HikariDataSource的创建问题
在之前的文章中,我们是每次创建 HikariDataSource 后,用完就关闭了。
示例代码:
private static HikariDataSource getDataSource() throws SQLException { HikariConfig config = new HikariConfig(); config.setJdbcUrl("jdbc:mysql://127.0.0.1:3306/javablog?useUnicode=true&characterEncoding=utf8&useSSL=false"); config.setUsername("root"); config.setPassword("123456"); config.addDataSourceProperty("cachePrepStmts", "true"); config.addDataSourceProperty("prepStmtCacheSize", "250"); config.addDataSourceProperty("prepStmtCacheSqlLimit", "2048"); return new HikariDataSource(config); } public static ResultSet simpleQuery(String sql) { try { HikariDataSource dataSource = getDataSource(); Connection connection = dataSource.getConnection(); Statement statement = connection.createStatement(); ResultSet resultSet = statement.executeQuery(sql); if (connection != null && !connection.isClosed()) connection.close(); if (dataSource != null && !dataSource.isClosed()) dataSource.close(); return resultSet; } catch (Exception e) { e.printStackTrace(); } return null; }
我们每次执行查询 simpleQuery 方法时,都会创建一个新的 HikariDataSource,并在最后关闭了 HikariDataSource 。
这样的问题是什么?
每次创建新的 HikariDataSource,都会创建 连接池,连接池中的线程越来越多,导致内存耗尽。
通过mysql的 show full processlist; 可以观察到数据库连接并没有增多,因为每次都关闭了。
程序崩溃的原因是连续创建的对象,垃圾回收器没有及时回收,导致内存耗尽了。
我们新建一个程序来测试一下,创建方法参照 http://blog.sqber.com/articles/HikariCP-Simple-Demo.html
我们无限循环调用查询方法,看程序情况,如下图代码:
main函数的代码:
while(true) { System.out.println(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date())); String sql = "select * from ResourceItem where status = 1"; simpleQuery(sql);//这里就不打印 ResultSet 中的内容了 }
在运行之前,我们设置一下 jvm 的运行参数
打开 “Run Configurations”窗口(我的程序是pool下面的Test01文件 ),选择“Arguments”
设置 VM arguments 参数,参数内容为:
-XX:+HeapDumpOnOutOfMemoryError
-Xms20m
-Xmx20m
-XX:+HeapDumpOnOutOfMemoryError :代表内存溢出时生成 dump 文件,我们可以使用 MAT (MemoryAnalyzer)来分析内存情况。
-Xms20m : 最小内存 20M
-Xmx20m : 最大内存 20M
即我们JVM内存就是20M,在内存20M的环境下运行,看情况如何。
设置好之后,我们点击 Run 按钮。
在控制台中,我们看到好多的 HikariPool-编号,从后面的编号得知,我们创建了太多的对象,导致内存耗尽了,并同时生成了一个 dump 文件。
因此,每次关闭并创建数据源是不可取的,这样会导致生成大量的对象,如果在GC没有回收之前对象过多,就会导致内存消耗很大。
当然,如果在内存消耗完毕之前,GC进行了回收,那样也没有问题。
但是仍然建议,不要重复创建对象。
我们创建一个单例
class DataSource { private DataSource() {} private static class SingletonHolder{ private static HikariDataSource instance = getDataSource(); private static HikariDataSource getDataSource() { HikariConfig config = new HikariConfig(); config.setJdbcUrl("jdbc:mysql://127.0.0.1:3306/javablog?useUnicode=true&characterEncoding=utf8&useSSL=false"); config.setUsername("root"); config.setPassword("123456"); config.addDataSourceProperty("cachePrepStmts", "true"); config.addDataSourceProperty("prepStmtCacheSize", "250"); config.addDataSourceProperty("prepStmtCacheSqlLimit", "2048"); return new HikariDataSource(config); } } public static HikariDataSource getInstance(){ return SingletonHolder.instance; } }
然后修改查询方法,数据源从单例获取,并不再关闭数据源。
public static ResultSet simpleQuery(String sql) { try { HikariDataSource dataSource = DataSource.getInstance(); Connection connection = dataSource.getConnection(); Statement statement = connection.createStatement(); ResultSet resultSet = statement.executeQuery(sql); if (connection != null && !connection.isClosed()) connection.close(); return resultSet; } catch (Exception e) { e.printStackTrace(); } return null; }
然后,再次运行,查看效果。这次在控制台,没有了 Pool-编号 的 Starting 和 Shut Down 了,有的只是输出的内容。
且运行一直没有报错。
*昵称:
*邮箱:
个人站点:
*想说的话: