HikariCP - HikariDataSource的创建

时间 2018/8/31 22:54:53 加载中...

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

我们无限循环调用查询方法,看程序情况,如下图代码:


image.png


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”


image.png


设置 VM arguments 参数,参数内容为:

-XX:+HeapDumpOnOutOfMemoryError

-Xms20m

-Xmx20m


-XX:+HeapDumpOnOutOfMemoryError :代表内存溢出时生成 dump 文件,我们可以使用 MAT (MemoryAnalyzer)来分析内存情况。

-Xms20m : 最小内存 20M

-Xmx20m : 最大内存 20M


即我们JVM内存就是20M,在内存20M的环境下运行,看情况如何。


image.png


设置好之后,我们点击 Run 按钮。


在控制台中,我们看到好多的 HikariPool-编号,从后面的编号得知,我们创建了太多的对象,导致内存耗尽了,并同时生成了一个 dump 文件。


image.png


因此,每次关闭并创建数据源是不可取的,这样会导致生成大量的对象,如果在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 了,有的只是输出的内容。

且运行一直没有报错。

扫码分享
版权说明
作者:SQBER
文章来源:http://blog.sqber.com/articles/the-create-of-HikariDataSource.html
本文版权归作者所有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。