Java内存泄漏,破坏/终止对象

时间:2010-09-27 11:51:27

标签: java constructor garbage-collection destructor

我遇到了内存泄漏,其代码类似于下面的代码(这是在每个循环中使用不同输入的模拟)。

问题

对象 Object_XXX 非常复杂,与数据库和其他对象的连接也填充了来自数据库的数据。

    for(int i=0; i<MAX; i=i+1){
        Class_XXX Object_XXX = new Class_XXX(Arg_1, Arg_2);

        // action with Object_XXX
    }

现在,在调用了几个方法之后, Object_XXX 也可以被丢弃,因为下一个循环将需要一个具有不同特征的对象(不同的数组,数组的大小,嵌套对象......)

构造函数类似于下面的构造函数,其他类具有类似的构造函数。

public Class_XXX(Arg_1, Arg_2, DB_Connection){

    try {
        Statement Query_Statement = null;
        ResultSet Query_ResultSet = null;
        String Query_String = null;

        Query_String = "...";
        Query_Statement = DB_Connection.createStatement();
        Query_ResultSet = Query_Statement.executeQuery(SQL);

        while (Query_ResultSet .next()) {
            this.Param_1 = Query_ResultSet .getString("param_1");
    this.Param_2 = Query_ResultSet .getString("param_2");
            ...
    this.Param_n = Query_ResultSet .getString("param_n");
        }
    } catch (SQLException e) {
        System.out.println("SQL Exception: "+ e.toString());
    }
}

问题

在这种情况下,最正确的方法是什么? a)在循环结束时完成Object_XXX b)在代码中没有使用构成Object_XXX的每一个对象时,最终确定它们? 我个人更喜欢a)因为我认为这会让垃圾收集器工作而不会弄乱它太多了

您是否也可以提供代码示例或参考?

谢谢!


第二轮:

在下面找到答案并查看其他页面(http://accu.org/index.php/journals/236)后,这是我现在用于构造函数的模板。太早看它是否有效。仍然存在“ exception.toString ”,但实际代码会在异常情况下为变量提供标准值,并在日志中报告操作。

public Class_XXX(String Object_Name, java.sql.Connection Query_Connection){

    try{ // begin try-catch for Query_Connection
        Statement Query_Statement = Query_Connection.createStatement();
        try { // begin try-finally for Query_Statement
            String Query_String = "SELECT param_1, param_2, ... param_3 FROM table_name WHERE object_name = '" + Object_Name + "'";
            ResultSet Query_ResultSet = Query_Statement.executeQuery(Query_String);
            try { // begin try-finally for Query_ResultSet

                while (Query_ResultSet.next()) {
                    this.Param_1 = Query_ResultSet.getString("param_1");
                    this.Param_2 = Query_ResultSet.getString("param_2");
                    // ...
                    this.Param_n = Query_ResultSet.getString("param_n");
                }

            } finally {
                try { Query_ResultSet.close(); }
                catch (SQLException ex) { System.out.println("Error in Class_XXX constructor - " + ex.toString()); }
            } // end try-finally for Query_ResultSet

        } finally {
            try { Query_Statement.close(); }
            catch (SQLException ex) { System.out.println("Error in Class_XXX constructor - " + ex.toString()); }
        } // end try finally for Query_Statement

    } catch(SQLException ex) {
            System.out.println("Error in Class_XXX constructor - " + ex.toString());
    } // end try-catch for Query_Connection

}

4 个答案:

答案 0 :(得分:4)

如果某些内容仍然存在引用,则终结对象不会将其从内存中删除。如果没有任何内容可供参考,垃圾收集器无论如何都会删除它,所以最终确定在这里没有多大意义。

如果您遇到内存泄漏,必须持续引用您的对象,因此无法对其进行垃圾回收。我建议使用一些分析器来查看它是什么。

答案 1 :(得分:2)

看起来你是一名习惯于做RAII的C ++程序员。

Java不支持具有与C ++等效语义的RAII,因为对象破坏由垃圾收集器完成,有时在对象变得无法访问之后,在单独的后台线程中。因此,几乎没有人使用finalize方法(否则将成为C ++析构函数的等价物)来释放资源。

对于只占用内存的对象,Java不需要RAII,因为它们的内存将在垃圾收集器无法访问后的某个时间自动回收。特别是,没有必要明确释放成员。

管理除内存以外的资源(如文件描述符)或希望立即执行清理工作的对象通常会提供一个必须显式调用的清理方法。正如您从他们的javadoc中看到的那样,StatementResultSet的实例是这样的对象(它们指的是vm之外的资源,需要及时发布)。以异常安全的方式调用清理方法的典型模式是:

Statement statement = connection.createStatement();
try {
    ResultSet resultset = statement.executeQuery(sql);
    try {
        // read the resultset
    } finally {
        resultset.close();
    }
} finally {
    statement.close();
}

还有一些风格问题:

  • exception.toString()仅包含异常消息。 exception.printStackTrace()另外打印整个堆栈跟踪。
  • 几乎每个java程序员都遵循以下惯例:包名以小写字母开头,类名以大写字母开头,字段/变量以小写字母开头。此外,单词通常使用camel-case而不是_分隔。

答案 2 :(得分:1)

建议不要使用最终化方法。 你最好把它留给垃圾收集器。 但是,您应该释放嵌套资源(关闭它们,指定null)。

答案 3 :(得分:1)

男孩,什么是构造函数。

你已经分配了很多局部变量,并没有向我们展示任何释放它们的代码,例如。

    Statement Query_Statement = null;
    ResultSet Query_ResultSet = null;

虽然在任何情况下你都不应该自己调用finalize,但是在这里调用它无论如何也无济于事,因为它无法访问构造函数中声明的局部变量。

学会遵循这种模式:

final Statement stmt = createStatement( );

try
{
  useStatement( stmt );
}
finally
{
  stmt.close( );
}

这是防止资源(不仅仅是内存)泄漏的唯一方法。

另外,以你的方式吞下异常是一个非常糟糕的主意。