GVKun编程网logo

Android SQLite内存泄漏

13

本篇文章给大家谈谈AndroidSQLite内存泄漏,同时本文还将给你拓展android–使用Context的静态内存泄漏、android–内存泄漏终结器错误、android–怀疑内存泄漏、Andro

本篇文章给大家谈谈Android SQLite内存泄漏,同时本文还将给你拓展android – 使用Context的静态内存泄漏、android – 内存泄漏终结器错误、android – 怀疑内存泄漏、Android 内存优化以及内存泄漏分析工具等相关知识,希望对各位有所帮助,不要忘了收藏本站喔。

本文目录一览:

Android SQLite内存泄漏

Android SQLite内存泄漏

我知道这是一个很长的帖子。请不要介意。

Leak foundE/Database( 4549): java.lang.IllegalStateException: mPrograms size 1E/Database( 4549):  at android.database.sqlite.SQLiteDatabase.finalize(SQLiteDatabase.java:1668)E/Database( 4549):  at dalvik.system.NativeStart.run(Native Method)E/Database( 4549): Caused by: java.lang.IllegalStateException: /data/data/com.rjblackbox.droid.fvt/databases/fvt.db SQLiteDatabase created and never closedE/Database( 4549):  at android.database.sqlite.SQLiteDatabase.<init>(SQLiteDatabase.java:1694)E/Database( 4549):  at android.database.sqlite.SQLiteDatabase.openDatabase(SQLiteDatabase.java:738)E/Database( 4549):  at android.database.sqlite.SQLiteDatabase.openOrCreateDatabase(SQLiteDatabase.java:760)E/Database( 4549):  at android.database.sqlite.SQLiteDatabase.openOrCreateDatabase(SQLiteDatabase.java:753)E/Database( 4549):  at android.app.ApplicationContext.openOrCreateDatabase(ApplicationContext.java:473)E/Database( 4549):  at android.content.ContextWrapper.openOrCreateDatabase(ContextWrapper.java:193)E/Database( 4549):  at android.database.sqlite.SQLiteOpenHelper.getWritableDatabase(SQLiteOpenHelper.java:98)E/Database( 4549):  at com.rjblackbox.droid.fvt.FVTDataHelper.<init>(FVTDataHelper.java:37)E/Database( 4549):  at com.rjblackbox.droid.fvt.FVTNotificationService.getNextEntry(FVTNotificationService.java:91)E/Database( 4549):  at com.rjblackbox.droid.fvt.FVTNotificationService.access$2(FVTNotificationService.java:90)E/Database( 4549):  at com.rjblackbox.droid.fvt.FVTNotificationService$1.run(FVTNotificationService.java:53)E/Database( 4549):  at android.os.Handler.handleCallback(Handler.java:587)E/Database( 4549):  at android.os.Handler.dispatchMessage(Handler.java:92)E/Database( 4549):  at android.os.Looper.loop(Looper.java:123)E/Database( 4549):  at android.app.ActivityThread.main(ActivityThread.java:4363)E/Database( 4549):  at java.lang.reflect.Method.invokeNative(Native Method)E/Database( 4549):  at java.lang.reflect.Method.invoke(Method.java:521)E/Database( 4549):  at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:860)E/Database( 4549):  at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:618)E/Database( 4549):  at dalvik.system.NativeStart.main(Native Method)

这是我的内存泄漏的堆栈跟踪。以下是我的数据助手的源代码。除了 Activity之外 ,我还有一个 Service可以
访问数据。对于经验丰富的开发人员来说,这一定是小菜一碟。

public class FVTDataHelper {    private static final String DB_NAME = "fvt.db";    private static final int DB_VERSION = 1;    private static final String TABLE_TIMERS = "timers";    private static final String[] TABLE_TIMERS_COLUMNS = {"id", "name", "added", "expires"};    private static final String TIMER_INSERT = "INSERT INTO " +     TABLE_TIMERS + "(''name'', ''added'', ''expires'') VALUES (?, ?, ?)";    private SQLiteDatabase db;    private Cursor c;    private SQLiteStatement timerInsert;    //Constructor    public FVTDataHelper(Context ctx) {        OpenHelper oh = new OpenHelper(ctx);        db = oh.getWritableDatabase();        timerInsert = db.compileStatement(TIMER_INSERT);    }    public void addTimerEntry(TimerEntry entry) {        String name = entry.getName();        long added = entry.getAdded();        long expires = entry.getExpires();        timerInsert.bindString(1, name);        timerInsert.bindString(2, Long.toString(added));        timerInsert.bindString(3, Long.toString(expires));        timerInsert.executeInsert();    }    public List<TimerEntry> getTimerEntries() {        ArrayList<TimerEntry> entries = new ArrayList<TimerEntry>();        c = db.query(TABLE_TIMERS, TABLE_TIMERS_COLUMNS, null, null, null, null, "expires asc");        if(c.moveToFirst()) {            int id;            String name;            long added;            long expires;            do {                id = c.getInt(0);                name = c.getString(1);                added = c.getLong(2);                expires = c.getLong(3);                if((System.currentTimeMillis() - added) >= 0) {                    entries.add(new TimerEntry(id, name, added, expires));                }            } while(c.moveToNext());        }        c.close();        return entries;    }    public void deleteTimerEntry(int id) {        db.delete(TABLE_TIMERS, "id=" + id, null);    }    //Helper class for creating and updating database     private static class OpenHelper extends SQLiteOpenHelper {        private Context ctx;        public OpenHelper(Context ctx) {            super(ctx, DB_NAME, null, DB_VERSION);            this.ctx = ctx;        }        @Override        public void onCreate(SQLiteDatabase db) {            String sqlDump = getSQLDump();            String[] statements = sqlDump.split("\n");            for(String statement : statements) {                if(DEBUG) Log.d(TAG, statement);                db.execSQL(statement);            }        }        @Override        public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {            onCreate(db);        }        //Helper: Returns SQL statements from the dump        private String getSQLDump() {            StringBuilder sb = new StringBuilder();            try {                InputStream is = ctx.getAssets().open("dump.sql");                int c;                while((c = is.read()) != -1) {                    sb.append((char) c);                }                is.close();            } catch (IOException e) {                Log.d(TAG, e.getMessage());            }            return sb.length() > 0 ? sb.toString() : ";";        }    }}

答案1

小编典典

您需要先关闭数据库,然后再重新打开它,堆栈跟踪消息很清楚地说明了问题所在。

一种解决方案是利用android.app.Application该类并将打开的数据库实例存储在其中。如果执行此操作,请确保使用Application上下文而不是Activity上下文打开数据库,否则在销毁活动时可能会泄漏上下文。

另一种解决方案是在onDestroy / onStop等中关闭数据库,然后在onCreate / onStart等中重新打开数据库。

android – 使用Context的静态内存泄漏

android – 使用Context的静态内存泄漏

我一直在做一些研究,如果这可能导致一个基础,我仍然不是100%肯定
内存泄漏.我正在使用按钮视图(v.context).我认为我没关系,因为上下文没有存储为静态,但如果可能的话,我想要一些反馈.我看到的主要问题是OSMonitor ……(M)值上升,上升和上升.每次打开/关闭小部件和屏幕旋转.

32M
43M
61M
77M
等等…

我不确定(M)是兆字节还是Megebits.如果这是基于堆栈,我假设Megebits perhpas,因为大多数高端设备限制在堆栈上的32/48 MB(或其他东西).

感谢您的反馈/额外的眼睛!

这是市场上的Banner应用程序,顺便说一下……

public class Globals {

public static final String  PREF_NAME       = "BannerPreferences";
public static final int     MAX_TEXT_SIZE   = 20;

// refresh ALL widgets loaded on the user''s screens
// this Could be for removing or adding ''pendingIntents or during bootup
public static void refreshAllWidgets(Context context) {
    Logger.d("BANNER","Globals:refreshAllWidgets");

    invalidateWidgets(context,BannerWidget.class); // 1x4
    invalidateWidgets(context,BannerWidget1x2.class);
    invalidateWidgets(context,BannerWidget2x2.class);
}

// there has to be a API way to do this!! Until then,just loop thru all
// widget_provider classes..
private static void invalidateWidgets(Context context,Class<?> cls) {

    ComponentName comp = new ComponentName(context,cls);
    AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context);

    int[] appWidgetIds = appWidgetManager.getAppWidgetIds(comp);

    for (int i = 0; i < appWidgetIds.length; i++) {
        BannerWidgetBase.updateAppWidget(context,appWidgetManager,appWidgetIds[i]);
    }

    appWidgetIds = null;
}

解决方法

没有泄漏.由于Dalvik VM的性质,堆在使用时会持续增长,直到达到最大堆大小.但是,堆中可能有足够的空间用于对象.我建议在仿真器映像中限制进程内存(堆),看看你是否真的得到了OutOfMemoryError.创建模拟器时,您要设置一个属性“Max VM application heap size”,例如到32(以兆字节为单位).

如果你得到一个OutOfMemoryError,你应该仔细看看Eclipse MAT.

P.S.:刚刚意识到你应该在你的情况下使用应用程序上下文,而不是活动.如果从活动中触发它,请考虑getApplicationContext而不是将活动作为上下文传递.静态内容可能比Activity实例寿命长.

android – 内存泄漏终结器错误

android – 内存泄漏终结器错误

我一直在研究内存泄漏并使用内存分析器工具来检查它们.所以,作为一种做法,我有以下代码泄漏活动,因为匿名内部类持有对活动的引用.这是代码:
public class MainActivity extends Activity {

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    exampleOne();
  }

  private void exampleOne() {
    new Thread() {
      @Override
      public void run() {
        while (true) {
          SystemClock.sleep(1000);
        }
      }
    }.start();
  }
}

我在这里有上述泄漏的记忆分析仪图像(6次旋转):

很明显,有6个运行线程持有对外部活动的隐式引用,从而防止它被垃圾收集.

现在,请考虑以下代码:

public class MainActivity extends Activity {

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    exampleTwo();
  }

  private void exampleTwo() {
    new MyThread().start();
  }

  private static class MyThread extends Thread {
    @Override
    public void run() {
      while (true) {
        SystemClock.sleep(1000);
      }
    }
  }
}

在这里,我已经使类成为静态,因此没有对外部活动的引用,GC可以自由地回收Activity对象而不会被线程类阻止.

以下是相同的MAT屏幕截图:

我对第二组截图感到困惑,其中有5个终结器引用.我搜索了它,发现JVM一旦将要进行GCed就将对象添加到引用队列中.我预计,尽管会发生这种情况,但MAT中不会提供这些更改,因为我认为GC不会花费太多时间来释放这些参考.即使我使用13次旋转,结果也是相同的,有12个终结器参考.我可能错了,但我认为MAT只显示1个Activity对象,因为其他人必须已经GCed.有关最终化参考队列的任何帮助,以及在垃圾收集过程中继续进行的过程.谢谢.

解决方法

选择Finalizer概述.它提供有关等待终结器运行的对象数量以及终结器线程的其他相关信息的信息.

android – 怀疑内存泄漏

android – 怀疑内存泄漏

我正在使用Eclipse的内存分析器来分析我的应用程序的堆转储,因为我认为我在某处遇到了内存泄漏.我不太确定要寻找什么,但MAT中的Leak SUSPECT报告显示了4个“问题嫌疑人”,其中包括:

 The class "org.apache.harmony.luni.internal.net.www.protocol.jar.JarURLConnectionImpl", loaded by "<system class loader>", occupies 608,976 (16.15%) bytes. The memory is accumulated in one instance of "java.util.jar.JarFile" loaded by "<system class loader>".

 One instance of "org.apache.harmony.xml.ExpatParser" loaded by "<system class loader>" occupies 501,304 (13.29%) bytes. The memory is accumulated in one instance of "java.lang.Object[]" loaded by "<system class loader>".

 127 instances of "org.bouncycastle.jce.provider.X509CertificateObject", loaded by "<system class loader>" occupy 451,280 (11.97%) bytes. These instances are referenced from one instance of "java.util.Hashtable$HashtableEntry[]", loaded by "<system class loader>"

 6,608 instances of "java.lang.String", loaded by "<system class loader>" occupy 407,824 (10.81%) bytes. 

我猜的最后一个是我使用了太多字符串?其他我不知道.我没有使用任何加密,所以我不知道为什么BouncyCastle会出现.

我能想到的唯一代码是造成“嫌疑人”的原因是:

 final InputStream stream = new URL(FeedUrl).openConnection().getInputStream();

 Xml.parse(stream, Xml.Encoding.UTF_8, root.getContentHandler());
 stream.close();

我正在解析一些不同大小的远程XML文件(使用SAX),但不超过1MB.此代码是解析大约5-6个xml文件的循环的一部分.

任何关于“问题可疑”是什么的见解,如果它们导致内存泄漏以及修复它的方法,都将非常感激.

解决方法:

问题可疑就是:第一个寻找内存泄漏的地方.它们是对象类型的应用程序中内存的最大用途.您很可能没有内存泄漏,但您可以通过首先查看这些对象来尝试减少内存使用量.

以下是一些包含更多信息的资源

> http://wiki.eclipse.org/index.php/MemoryAnalyzer
> http://live.eclipse.org/node/520
> http://memoryanalyzer.blogspot.com/

Android 内存优化以及内存泄漏分析工具

Android 内存优化以及内存泄漏分析工具

假设有一个单例的ListenerManager, 可以add / remove Listener, 有一个Activity, 实现了该listener, 且这个Activity中持有大对象BigObject, BigObject中包含一个大的字符串数组和一个Bitmap List.

代码片段如下:

ListenerManager

 1 public class ListenerManager {
 2 
 3     private static ListenerManager sInstance;
 4     private ListenerManager() {}
 5 
 6     private List<SampleListener> listeners = new ArrayList<>();
 7 
 8     public static ListenerManager getInstance() {
 9         if (sInstance == null) {
10             sInstance = new ListenerManager();
11         }
12 
13         return sInstance;
14     }
15 
16     public void addListener(SampleListener listener) {
17         listeners.add(listener);
18     }
19 
20     public void removeListener(SampleListener listener) {
21         listeners.remove(listener);
22     }
23 }

MemoryLeakActivity

 1 public class MemoryLeakActivity extends AppCompatActivity implements SampleListener {
 2 
 3     private BigObject mBigObject = new BigObject();
 4 
 5     @Override
 6     protected void onCreate(Bundle savedInstanceState) {
 7         super.onCreate(savedInstanceState);
 8         setContentView(R.layout.activity_memory_leak);
 9 
10         ListenerManager.getInstance().addListener(this);
11     }
12 
13     @Override
14     public void doSomething() {
15 
16     }
17 }

BigObject

 1 public class BigObject {
 2 
 3     private ArrayList<Bitmap> bitmaps = new ArrayList<>();
 4     private String[] values = new String[1000];
 5 
 6     public BigObject() {
 7 
 8         for (int i = 0; i < 20; i++) {
 9             bitmaps.add(Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888));
10         }
11 
12         for (int i = 0; i < 1000; i++) {
13             values[i] = "value:" + i;
14         }
15     }

1. Memory Monitor

Memory Monitor 是 Android Studio内置的, 官方的内存监测工具. 图形化的展示当前应用的内存状态, 包括已分配内存, 空闲内存, 内存实时动态等.

  • 图形区域:

    • 横向时间轴, 内存检测时间, 跟随滚动.
    • 纵向内存轴, 内存使用量, 根据应用使用动态分配.
    • 蓝色区域表示当前已分配使用的内存量.
    • 灰色区域表示剩余可使用的内存量.
    • 红色圈圈指示的是系统GC事件(内存有一定量的回收).
  • 工具栏:

    • ① GC按钮, 点击执行一次GC操作.
    • ② Dump Java Heap按钮, 点击会在该调试工程的captures目录生成一个类似这样"com.anly.githubapp_2016.09.21_23.42.hprof"命名的hprof文件, 并打开Android Studio的HPROF Viewer显示该文件内容.
    • ③ Allocation Traking按钮, 点击一次开始, 再次点击结束, 同样会在captrures目录生成一个文件, 类似"com.anly.githubapp_2016.09.21_23.48.alloc", alloc后缀的文件, 并打开Allocation Tracker视图展示该文件内容.

1.1 查看Memory使用, 并导出hprof文件

Memory Monitor通过Dump Java Heap可以生成一个hprof的文件, 这个文件是Android特定的Heap和CPU分析文件, 记录了这段时间内的Java Heap变化.

关于Java Heap

由Java Heap文件可以看到如下数据:

  • 按类型显示对象申请的内存快照(内存大小);
  • 每次自动或手动触发GC时的样本数据;
  • 协助定位可能发生的内存泄露点:
    • 所有已经被destroyed的activity, 还可以从GC Root访问到.
    • 重复的String实例.

启动我们要检测的Activity(MemoryLeakActivity), 然后退出, 在monitor中查看内存变化:

1.2 在HPROF Viewer界面, 开始分析

可点击选择Heap类型, Heap类型分为:

  • App Heap -- 当前App使用的Heap
  • Image Heap -- 磁盘上当前App的内存映射拷贝
  • Zygote Heap -- Zygote进程Heap(每个App进程都是从Zygote孵化出来的, 这部分基本是framework中的通用的类的Heap)

第一步

点击"Analyzer Tasks"视图中的启动按钮, 启动分析

分析任务包括:

  • 检测泄露的Activity
  • 查找重复的String实例

第二步

查看"Analysis Result"中的分析结果, 点击"Leaked Activityes"中的具体实例, 该实例的引用关系将会展示在"Reference Tree"视图中.

第三步

根据"Reference Tree"视图中的引用关系找到是谁让这个leak的activity活着的, 也就是谁Dominate这个activity对象.

此例中, 比较简单, 可以很清晰看到是ListenerManager的静态单例sInstance最终支配了MemoryLeakActivity. sIntance连接到GC Roots, 故而导致MemoryLeakActivity GC Roots可达, 无法被回收.

1.3 使用Heap Viewer查看内存消耗

上述步骤, 可以让我们快速定位可能的内存泄露. 当然, 内存问题, 除了内存泄露, 还有内存消耗过大. 我们可以在Heap Viewer中查看分析内存的消耗点, 如下:

4, MAT

Eclipse MAT是一个快速且功能丰富的Java Heap分析工具, 可以帮助我们寻找内存泄露, 减少内存消耗.

MAT可以分析程序(成千上万的对象产生过程中)生成的Heap dumps文件, 它会快速计算出对象的Retained Size, 来展示是哪些对象没有被GC, 自动生成内存泄露疑点的报告.

MAT. 相对与Android Studio中的Memory Monitor, HPROF工具来说, MAT的使用显得更加生涩, 难以理解.

关于MAT的帮助文档, 个人翻译了一份, 需要的同学戳这里.

当然, 如果我们想对内存的使用相关知识了解得更多, 还是有必要了解下MAT的...
下面我们以几个角度来了解下MAT的基本使用:

再次:
Android Studio导出的hprof文件需要转换下才可以在MAT中使用.

终端输入: 
1 $ hprof-conv com.anly.samples_2016.10.31_15.07.hprof mat.hprof

4.1 Histogram视图定位内存消耗

MAT中很多视图的第一行, 都可以输入正则, 来匹配我们关注的对象实例.

4.2 Dominate Tree视图查看支配关系

4.3 使用OQL查询相关对象

对于Android App开发来说, 大部分的内存问题都跟四大组件, 尤其是Activity相关, 故而我们会想查出所有Activity实例的内存占用情况, 可以使用OQL来查询:

具体OQL语法看这里.

4.4 GC路径定位问题

上面几个视图都可以让我们很快速的找到内存的消耗点, 接下来我们要分析的就是为何这些个大对象没有被回收.

根据第一弹:GC那些事儿所言, 对象没有被回收是因为他有到GC Roots的可达路径. 那么我们就来分析下这条路径(Path to GC Roots), 看看是谁在这条路中"搭桥".

如下, 进入该对象的"path2gc"视图:

同样, 与HPROF Analyzer异曲同工, 找出了是ListenerManager的静态实例导致了MemoryLeakActivity无法回收.

5. LeakCanary让内存泄露无处可藏

大道至简, 程序员都应该"懒", 故而我们都希望有更方便快捷的方式让我们发现内存泄露. 伟大的square发挥了这一优良传统, LeakCanary面世.

5.1 加入LeakCanary

app的build.gradle中加入:

1 dependencies {
2    debugCompile ''com.squareup.leakcanary:leakcanary-android:1.5''
3    releaseCompile ''com.squareup.leakcanary:leakcanary-android-no-op:1.5''
4    testCompile ''com.squareup.leakcanary:leakcanary-android-no-op:1.5''
5 }

Application中加入:

1 public class SampleApplication extends Application {
2 
3     @Override
4     public void onCreate() {
5         super.onCreate();
6 
7         LeakCanary.install(this);
8     }
9 }

4.2 操作要检测的界面, 查看结果

当发生可疑内存泄露时, 会在桌面生成一个"Leaks"的图标, 点击进去可以看到内存泄露的疑点报告:

 

我们今天的关于Android SQLite内存泄漏的分享已经告一段落,感谢您的关注,如果您想了解更多关于android – 使用Context的静态内存泄漏、android – 内存泄漏终结器错误、android – 怀疑内存泄漏、Android 内存优化以及内存泄漏分析工具的相关信息,请在本站查询。

本文标签: