数据源的Java内存泄漏

数据源的Java内存泄漏,java,memory-leaks,jvm,datasource,Java,Memory Leaks,Jvm,Datasource,我多次听说,不关闭数据库连接可能会导致内存泄漏 (例如本节) 我试图通过从org.apache.commons.dbcp2.BasicDataSource获取连接来重现同样的问题 而不是关闭它 这是我的代码: //Repo class private final BasicDataSource ds; public Repo() { ds = new BasicDataSource(); ds.setDriverClassName("org.postgresql.Dr

我多次听说,不关闭数据库连接可能会导致内存泄漏
(例如本节)

我试图通过从org.apache.commons.dbcp2.BasicDataSource获取连接来重现同样的问题 而不是关闭它

这是我的代码:

//Repo class
private final BasicDataSource ds;
public Repo() {
    ds = new BasicDataSource();

    ds.setDriverClassName("org.postgresql.Driver");
    ds.setUrl("jdbc:postgresql://localhost/postgres");
    ds.setUsername("postgres");
    ds.setPassword("postgres");
    ds.setMaxOpenPreparedStatements(10000);
    ds.setMaxTotal(10000);


}
public PreparedStatement prepStatement(String sql) throws SQLException {
    return this.ds.getConnection().prepareStatement(sql);
}

//Logic class
public JsonNode logic(String name) {
    PreparedStatement ps = this.repo.prepStatement("select data from public.users where name = ? ");
    ps.setString(1, name);
    //The rest of the logic - no close method or try with resource
}
我已经重复了差不多400次相同的过程,并且减小了初始堆大小和最大堆大小。
不过,没有内存泄漏的迹象。即使在VisualVM监控中,堆图也很正常:


关于如何重现这个问题有什么想法吗?

不关闭资源的问题不是潜在的内存泄漏,而是潜在的资源泄漏。我们讨论的是文件句柄、网络连接,但甚至是可能在数据库服务器端分配的资源,在JVM中根本不可见

实际的
PreparedStatement
实现是否具有终结器或清理器之类的保护措施,当对象被垃圾收集时,它们将关闭资源,这取决于特定的数据库驱动程序。但是,即使有,这也意味着资源将被保留,直到后续的垃圾收集周期识别出无法访问的对象并触发终结

在您的特定设置中,似乎每分钟都有一次垃圾收集。也许,关键的非内存资源在这些点上得到了清理;你甚至都没有检查它们

但是,即使已为此设置清理了这些资源,您也必须注意以下几点:

  • 并非每个数据库驱动程序都可以这样工作

  • 在实际的生产环境中,持有关键的非内存资源(如锁、文件句柄、数据库连接等)的时间比需要的时间长一分钟,这已经是一个巨大的问题

  • 不能保证你每分钟都有一次垃圾收集。一个系统可以在没有垃圾收集的情况下运行数小时甚至数天

  • 无法保证垃圾收集能够识别特定的不可访问对象。在一个简单的设置中,当下一次收集发生时,对象属于年轻一代,这可能会顺利工作,但现代并发收集器乐于在短时间内回收大量内存,并且可以配置时间限制,而不急于收集每个对象

    就记忆而言,每个物体的记忆都是相等的,因此,它是不相关的,哪些物体被这种“雄鹿的最大爆炸”收藏忽略了。因此,
    PreparedStatement
    实例可能是每个集合中被忽略的不幸的不可访问对象之一。这与几个字节无关,它会阻塞,这就是为什么这种策略是允许的。如前所述,问题在于它可能无限期地保留非内存资源


能否添加代码以保持连接打开?这可能取决于您使用的JDBC驱动程序,以及是否使用连接池。并不是说不关闭数据库连接总是会导致内存泄漏。我们也可以检查数据库端。然而,这里看起来应该尝试构造一个未准备好的SQL:
\“…name=”+name+“\''”
我试图通过不关闭连接来故意创建内存泄漏。