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

时间:2012-04-02 21:11:27

标签: java mysql loops jdbc memory-leaks

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

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()方法结束时所有对象都不符合垃圾回收的条件?每次我在探查器中调用垃圾收集时,我都会得到另一个幸存的一代。 感谢。

4 个答案:

答案 0 :(得分:3)

我会改变这个:

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

到此:

                        results = statement.executeQuery(rawQuery);

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

答案 1 :(得分:2)

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

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

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

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

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

JVisualVM Screenshot

答案 2 :(得分:0)

我建议你尝试两件事:

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

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

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

答案 3 :(得分:0)

在内存arg上添加堆转储,然后使用mat或类似内容查看堆。 Using HeapDumpOnOutOfMemoryError parameter for heap dump for JBoss

相关问题