梦魇java泄漏……用循环和jdbc

当我在探查器中运行以下代码时,我得到一个char []和byte [],直到程序崩溃,因为java堆内存不足exception。 有人可以告诉我为什么吗? 也许我正在做一些根本错误的事情。

package testleak; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.sql.Connection; import java.sql.DriverManager; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; import java.util.Properties; import javax.swing.Timer; public class TestLeak { static String DB_USERNAME = "userName"; static String DB_SUBSCRIPTION_EXPIRATION = "subscriptionExpiration"; static String DB_REMOTE_ACCESS_ENABLED = "remoteAccessEnabled"; static String DB_LOCAL_USERNAME = "root"; static String DB_LOCAL_PASS = "root"; public static void main(String[] args) { Timer timer = new Timer(2000, new ActionListener() { @Override public void actionPerformed(ActionEvent evt) { TestLeak tester = new TestLeak(); try { tester.go(); } catch (NumberFormatException n) { } tester = null; } }); timer.start(); while (true) { //keep the program from ending... } } private void go() throws NumberFormatException { ResultSet results = null; Connection conn = null; Properties connectionProps = new Properties(); try { connectionProps.put("user", "root"); connectionProps.put("password", "root"); conn = DriverManager.getConnection("jdbc:mysql://localhost:8889/myDataBase", connectionProps); connectionProps = null; try { String rawQuery = new String("SELECT " + TestLeak.DB_USERNAME + ", " + TestLeak.DB_REMOTE_ACCESS_ENABLED + ", " + TestLeak.DB_SUBSCRIPTION_EXPIRATION + " FROM myTable"); Statement statement = conn.createStatement(); try { statement.executeQuery(rawQuery); results = statement.getResultSet(); rawQuery = null; try { while (results.next()) { String auth = new String(results.getString(TestLeak.DB_REMOTE_ACCESS_ENABLED)); if (auth.equals("1")) { Long subExpires = Long.valueOf(results.getString(TestLeak.DB_SUBSCRIPTION_EXPIRATION)); if (subExpires > System.currentTimeMillis()) { System.out.println(results.getString(TestLeak.DB_USERNAME)); System.out.println(); } subExpires = null; } auth = null; } } finally { results.close(); } } finally { statement.close(); } } finally { conn.close(); } } catch (SQLException e) { System.out.println(e.getMessage()); } } } 

我想我正在释放一切,但必须防止所有对象被释放。 为什么go()方法结束时所有对象都不符合垃圾回收的条件? 每次我在探查器中调用垃圾收集时,我都会得到另一个幸存的一代。 谢谢。

遗憾的是,您没有指定有关该问题的一些详细信息,例如,结果集的大小(行数),以及运行内存不足exception需要多长时间。

我现在没有访问你拥有的mysql驱动程序,但我使用H2数据库运行相同的代码,myTable中有1000行。 在测试期间,JVM的堆大小稳定,没有任何内存泄漏。 您可以在附加的屏幕截图中看到。 堆大小稍微增加,然后在GC之后返回到原始位置,再次向上,再次向下,以非常稳定的模式。

您可以运行您的应用程序,然后运行Jvisualvm并连接到您的应用程序,以查看,例如,数据库中的结果数量是否太大,无法容纳到现有内存中。 这是我的猜测。 在这种情况下,蓝线将快速超过最大内存。

如果是这种情况,则使用-Xmx设置运行应用程序以增加内存大小。

如果确实存在内存泄漏,则它不在您的代码中,而是在您正在使用的驱动程序中。 为了确认内存泄漏,下图中的蓝线将上升(分配内存),GC将运行(释放内存),但蓝线永远不会回到原来的位置,留下一些物体。

JVisualVM截图

我会改变这个:

  statement.executeQuery(rawQuery); results = statement.getResultSet(); 

对此:

  results = statement.executeQuery(rawQuery); 

后者肯定是API批准的方法,虽然我不能肯定前者是一个问题,但它似乎可以创建两个单独的结果集,其中你只关闭一个。

我建议你尝试两件事:

将计时器延长至约10秒钟。 对于缓慢的系统,两个人期待很多。

在您的空闲循环中放置一个Thread.currentThread.sleep(10) (或类似的)。

我希望你不要等待go完成。 当您在空闲循环中旋转时,数据库连接因缺少循环而死亡,并且每两秒钟您添加另一个连接和查询。 难怪可怜的事情正在挣扎。

在内存arg上添加堆转储,然后使用mat或类似的方式查看堆。 使用HeapDumpOnOutOfMemoryError参数进行JBoss的堆转储