【问题标题】:Garbage Collection with temporary instances of classes带有临时类实例的垃圾收集
【发布时间】:2013-04-20 22:13:06
【问题描述】:

我对 android 开发相对较新,并且来自 ac# 背景,完全有可能我的整个策略是错误的,但是当我没有正确关闭数据库连接导致内存泄漏时,Eclipse 不断警告我.

我有一个扩展 SQLiteOpenHelper 的基础数据库类:

public class MySQLiteOpenHelper extends SQLiteOpenHelper {

    public MySQLiteOpenHelper(Context context, String name,
            CursorFactory factory, int version) {
        super(context, name, factory, version);
    }

    public MySQLiteOpenHelper(Context context) {
        this(context, "myDb", null, 1);
    }
    @Override
    public void onCreate(SQLiteDatabase db) {
        db.execSQL("CREATE TABLE MyTable (A INT)");
    }
    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {

    }

    public Cursor executeSelect(String sql, String[] parameters) {
        return getReadableDatabase().rawQuery(sql, parameters);
    }

}

一个通用实体:


public class MyClass {
    private int a;

    public void setA(int value) {
        this.a = value;
    }

    public int getA() {
        return this.a;
    }
}

本质上是MyClass 的服务(尽管实际上这扩展了一个通用抽象类以实现可重用性)


public class MyClassService {

    private MySQLiteOpenHelper helper;
    private Context context;

    public MyClassService(Context context) {
        this.context = context;
    }

    private MySQLiteOpenHelper getHelper() {
        if (helper == null) {
            helper = new MySQLiteOpenHelper(this.context);
        }
        return helper;
    }

    public void dispose() {
        if (helper != null) {
            helper.close();
            helper = null;
        }
    }
    public ArrayList<MyClass> getAll()
    {
        ArrayList<MyClass> list = new ArrayList<MyClass>();
        Cursor cursor = getHelper().executeSelect("SELECT A FROM MyTable", new String[0]);

        while (cursor.moveToNext()) {
            MyClass item = new MyClass()
            item.setA(cursor.getInt(0));
            list.add(item);
        }
        cursor.close();
        return list;
    }
}   

所以,我的问题是当我在 Activity 中使用这样的一行代码时:

ArrayList<MyClass> list = new MyClassService(this).getAll();

MyClassService 的实例被立即处理掉了,或者这可能是我的内存泄漏的根源。

我会更好地调用完整代码以确保使用 dispose 方法关闭数据库吗?

MyClassService svc = new MyClassService(this);
ArrayList<MyClass> list = svc.getAll();
svc.dispose();

【问题讨论】:

    标签: java android sqlite memory-leaks


    【解决方案1】:

    Garbage Collector 将能够收集您的类以及 Helper 类,因为它们不再是 Object 链 的一部分(不是技术术语 - 只是我编造的)。但是,您仍然需要显式关闭数据库(如果您不这样做,这肯定是您的内存泄漏罪魁祸首)。就目前而言,您可以在对象的 finalize() 方法中执行此操作,该方法在垃圾收集期间调用:

    @Override
    public void finalize() {
        dispose();
    }
    

    不过,我通常更喜欢做一些不同的事情。像这样的数据存储通常最好写成单例,因为它们可能被多个类访问,如果创建了不同的实例,它们仍然会打开一个新的访问点来读取一个写入,并可能导致许多问题。您有一种单例样式的设置,因为您的代码中有一个 helper 变量,但您可能只想让您的 Helper 类成为单例。你可以通过删除你的构造函数并添加这个来做到这一点:

    private static MySQLiteOpenHelper self;
    
    private MySQLiteOpenHelper(Context context, String name, CursorFactory factory, int version) {
        super(context, name, factory, version);
    }
    
    private MySQLiteOpenHelper(Context context) {
        this(context, "myDb", null, 1);
    }
    
    public static MySQLiteOpenHelper sharedHelper(Context context) {
        if (self == null)
            self = new MySQLiteOpenHelper(context);
        return self;
    }
    

    然后,您可以使用以下方法获取帮助器,而不是使用 MyClassService 跟踪帮助器对象:

    MySQLiteOpenHelper.sharedHelper(context);
    

    这样做的好处是您只需在整个应用程序中跟踪一个 Helper,并且在 helper 的 finalize() 方法中,您现在可以关闭数据库。这将在应用进程被杀死时调用,并防止任何内存泄漏:

    public void finalize()
    {
        close();
    }
    

    【讨论】:

    • 谢谢,您已经回答了我的问题并提出了改进建议。使用context.getApplicationContext() 而不仅仅是context(从使用this 的活动传递)初始化MySQLiteOpenHelper 有什么好处吗?
    • @GarethD,很高兴我能帮上忙!在 Android 文档中,它指出传递 Activity 上下文可能会导致内存泄漏 - 如果活动已关闭,则会发生这种情况,但帮助程序保留了指向其上下文的指针。另一方面,Application 上下文与应用程序相关联,如果被杀死,它也会垃圾收集您的助手类。也就是说,我在传递常规上下文时从未遇到任何问题 - 但使用 Application 上下文是最安全的(主要区别是您不能使用应用程序上下文修改 UI)。
    【解决方案2】:

    您忘记对从getReadableDatabase() 返回的数据库对象调用close

    【讨论】:

      【解决方案3】:

      您应该显式调用dispose - 不这样做不会导致内存泄漏(可以在new MyClassService(this).getAll() 之后立即收集对象,因为没有对该对象的任何实时引用),但可能会导致您的数据库用完可用的连接。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-08-28
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-03-09
        • 2020-11-29
        相关资源
        最近更新 更多