本篇文章给大家谈谈AndroidSQLite内存泄漏,同时本文还将给你拓展android–使用Context的静态内存泄漏、android–内存泄漏终结器错误、android–怀疑内存泄漏、Andro
本篇文章给大家谈谈Android SQLite内存泄漏,同时本文还将给你拓展android – 使用Context的静态内存泄漏、android – 内存泄漏终结器错误、android – 怀疑内存泄漏、Android 内存优化以及内存泄漏分析工具等相关知识,希望对各位有所帮助,不要忘了收藏本站喔。
本文目录一览:- Android SQLite内存泄漏
- android – 使用Context的静态内存泄漏
- android – 内存泄漏终结器错误
- android – 怀疑内存泄漏
- Android 内存优化以及内存泄漏分析工具
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的静态内存泄漏
内存泄漏.我正在使用按钮视图(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; }
解决方法
如果你得到一个OutOfMemoryError,你应该仔细看看Eclipse MAT.
P.S.:刚刚意识到你应该在你的情况下使用应用程序上下文,而不是活动.如果从活动中触发它,请考虑getApplicationContext而不是将活动作为上下文传递.静态内容可能比Activity实例寿命长.
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.有关最终化参考队列的任何帮助,以及在垃圾收集过程中继续进行的过程.谢谢.
解决方法
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 内存优化以及内存泄漏分析工具
假设有一个单例的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视图定位内存消耗
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 内存优化以及内存泄漏分析工具的相关信息,请在本站查询。
本文标签: