Android以内存泄漏调试学习与总结

你们有或常常碰到OOM的问题,对吧?不少这样的问题只要一出现相信你们的想法跟个人同样,就是本身的应用:优化、优化、再优化!并且若是出现相似于OOM这样级别的问题,根本就很差处理,LogCat日志中显示的信息仅仅是OOM,并不会给你提示如何解决的方法或思路,由于引发OOM的缘由是你应用的问题,不是系统问题!应该想下,在优化以前找到须要优化的地方,再去作优化操做不是更直接吗?相信大多数朋友应该常常听过或使用Jnuit调试吧,好了,废话很少说,今天就跟你们一块儿来学习总结下OOM的调试方法,来找到须要优化的地方,要知道OOM也是能够一步步调试的:javascript

Android(Java)中常见的容易引发内存泄漏的不良代码:

  1. 查询数据库没有关闭游标
    程序中常常会进行查询数据库的操做,可是常常会有使用完毕Cursor后没有关闭的状况。若是咱们的查询结果集比较小,对内存的消耗不容易被发现,只有在常时间大量操做的状况下才会复现内存问题,这样就会给之后的测试和问题排查带来困难和风险示例代以下码:
if (cursor.moveToNext()) { 
  ... ... 
} 
 修正示例代码: 
Cursor cursor = null; 
try { 
  cursor = getContentResolver().query(uri ...); 
  if (cursor != null && cursor.moveToNext()) { 
  ... ... 
  } 
} finally { 
  if (cursor != null) { 
  try { 
  cursor.close(); 
  } catch (Exception e) { 
  //ignore this 
  } 
  } 
} 
复制代码
  1. 构造Adapter时,没有使用缓存的 convertView
  2. Bitmap对象不在使用时调用recycle()没有及时释放
    若是一个Bitmap对象比较占内存,当它不在被使用的时候,能够调用Bitmap.recycle()方法回收此对象的像素所占用的内存
    4.没有及时释放对象的引用
    简单举个例子:好比两个Activity之间传递的Context 或其它的自定义对象,使用完后必须当即释放 即:Activity = null ; Context = null ; Object = null;能够的话在这释放对象以后通知系统来回收:System.gc();这样最好了!
    Android主要应用在嵌入式设备当中,而嵌入式设备因为一些众所周知的条件限制,一般都不会有很高的配置,特别是内存是比较有限的。若是咱们编写的代 码当中有太多的对内存使用不当的地方,不免会使得咱们的设备运行缓慢,甚至是死机。为了可以使得Android应用程序安全且快速的运行,Android 的每一个应用程序都会使用一个专有的Dalvik虚拟机实例来运行,它是由Zygote服务进程演变过来的,也就是说每一个应用程序都是在属于本身的进程中运行的(问题一:这个地方怎么知道是在属于本身的进程中运行的?你们继续,答案会在下面详细介绍)。一方面,若是程序在运行过程当中出现了内存泄漏的问题,仅仅会使得本身的进程被杀掉,而不会影响其余进程(若是是system_process 等系统进程出问题的话,则会引发系统重启)。另外一方面Android为不一样类型的进程分配了不一样的内存使用上限,若是应用进程使用的内存超过了这个上限, 则会被系统视为内存泄漏,从而被杀掉
    下面来解释下问题一:”每一个应用程序都是在属于本身的进程中运行的”这句话,对于这句话,你们只记住一点:“当一个程序第一次启动的时候,Android会启动一个LINUX进程和一个主线程。默认的状况下,全部该程序的组件都将在该进程和线程中运行。”~_ O_O !!!**

同时,Android会为每一个应用程序分配一个单独的LINUX用户。Android会尽可能保留一个正在运行进程,只在内存资源出现不足时,Android会尝试中止一些进程从而释放足够的资源给其余新的进程使用, 也能保证用户正在访问的当前进程有足够的资源去及时地响应用户的事件。Android会根据进程中运行的组件类别以及组件的状态来判断该进程的重要性,Android会首先中止那些不重要的进程。按照重要性从高到低一共有五个级别就是咱们常说的:前台进程、可见进程、服务进程、后台进程、空进程(此处概念略,你们本身查)
还有个小扩展:当一个程序第一次启动时,Android会同时启动一个对应的主线程(Main Thread),主线程主要负责处理与UI相关的事件,如用户的按键事件,用户接触屏幕的事件以及屏幕绘图事件,并把相关的事件分发到对应的组件进行处理。因此主线程一般又被叫作UI线程。在开发Android应用时必须遵照单线程模型的原则: Android UI操做并非线程安全的而且这些操做必须在UI线程中执行。Android的UI是单线程(Single-threaded)的。为了不拖住GUI,一些较费时的对象应该交给独立的线程去执行。若是幕后的线程来执行UI对象,Android就会发出错误讯息 CalledFromWrongThreadException。之后遇到这样的异常抛出时就要知道怎么回事咯!**php

** 好了,铺垫知识就写这么多了,下面直接进入主题了:OOM调试**java

方式一:使用内存监测工具 DDMS –> Heap:(真机、模拟器都可使用)数据库

** 1. 启动eclipse后,切换到DDMS透视图,并确认Devices视图、Heap视图都是打开的,没打开的直接Window>ShowView>本身选;**
** 2. 将手机经过USB连接至电脑,连接时须要确认手机是处于“USB调试”模式**缓存

** 3. 连接成功后,在DDMS的Devices视图中将会显示手机设备的序列号,以及设备中正在运行的部分进程信息;**安全

** 4. 点击选中想要监测的进程,若是在进程列表中未出现你的进程的话随便选中一条让Device一排的工具处于可用状态,再点击下Update Heap让其自动找到咱们跑的应用的进程,好比临时跑的两个应用进程如图eclipse



** 5. 点击Heap视图中的“Cause GC”按钮;**工具


** 6.点击Cause GC以后就能够看到咱们应用的内存状况以下图:**学习


说明:
a) 点击“Cause GC”按钮至关于向虚拟机请求了一次gc操做;
b) 当内存使用信息第一次显示之后,无须再不断的点击“Cause GC”,Heap视图界面会定时刷新,在对应用的不断的操做过程当中就能够看到内存使用的变化;
c) 内存使用信息的各项参数根据名称便可知道其意思,不知道具体意思的朋友自行用工具(有道、词霸查去)测试

** 知道工具使用了,那么如何才能知道咱们的程序是否有内存泄漏的可能性呢。这里须要注意一个值:Heap视图中部有一个Type叫作data object,即数据对象,也就是咱们的程序中大量存在的类类型的对象。在data object一行中有一列是“Total Size”,其值就是当前进程中全部Java数据对象的内存总量,若是你们想要看“Total Size”是分配的具体信息能够点击“data object这一行来查看详细信息,以下图”(你们看不清楚的点击看大图)**


通常状况下,在data object行的“Total Size”这个值的大小决定了是否会有内存泄漏。能够这样判断:
a) 不断的操做当前应用,同时注意观察data object的Total Size值;
b) 正常状况下Total Size值都会稳定在一个有限的范围内,也就是说因为程序中的的代码良好,没有形成对象不被垃圾回收的状况,因此说虽然咱们不断的操做会不断的生成不少对 象,而在虚拟机不断的进行GC的过程当中,这些对象都被回收了,内存占用量会会落到一个稳定的水平;
c) 反之若是代码中存在没有释放对象引用的状况,则data object的Total Size值在每次GC后不会有明显的回落,随着操做次数的增多Total Size的值会愈来愈大,
** 直到到达一个上限后致使进程被杀掉。**

** Android为应用进程分配的内存上限以下所示:(下面这些是网上查到的,不懂下面的语法,但知道有限制这么一回事就够了,此处不研究下面的代码)**

killed by the kernel. These are used in ActivityManagerService
  setprop ro.FOREGROUND_APP_ADJ 0
  setprop ro.VISIBLE_APP_ADJ 1
  setprop ro.SECONDARY_SERVER_ADJ 2
  setprop ro.BACKUP_APP_ADJ 2
  setprop ro.HOME_APP_ADJ 4
  setprop ro.HIDDEN_APP_MIN_ADJ 7
  setprop ro.CONTENT_PROVIDER_ADJ 14
  setprop ro.EMPTY_APP_ADJ 15
 Define the memory thresholds at which the above process classes will
 be killed. These numbers are in pages (4k)
 setprop ro.FOREGROUND_APP_MEM 1536
 setprop ro.VISIBLE_APP_MEM 2048
  setprop ro.SECONDARY_SERVER_MEM 4096
  setprop ro.BACKUP_APP_MEM 4096
  setprop ro.HOME_APP_MEM 4096
 setprop ro.HIDDEN_APP_MEM 5120
  setprop ro.CONTENT_PROVIDER_MEM 5632
 setprop ro.EMPTY_APP_MEM 6144
 Write value must be consistent with the above properties.
 Note that the driver only supports 6 slots, so we have HOME_APP at the
same memory level as services.
write /sys/module/lowmemorykiller/parameters/adj 0,1,2,7,14,15
  write /proc/sys/vm/overcommit_memory 1
 write /proc/sys/vm/min_free_order_shift 4
 write /sys/module/lowmemorykiller/parameters/minfree 1536,2048,4096,5120,5632,6144
 Set init its forked children’s oom_adj
write /proc/1/oom_adj -16
复制代码

方式二:

**内存监测工具 DDMS –> Heap **
**使用内存分析工具 MAT(Memory Analyzer Tool) **
(一) 生成.hprof文件 (生成很简单,直接点击Device 工具栏中的 Dump HPROF file便可生成)
**(二) 使用MAT导入.hprof文件 **
(三) 使用MAT的视图工具分析内存

** 总之,使用DDMS的Heap视图工具能够很方便的确认咱们的程序是否存在内存泄漏的可能性。这个地方顺带着也简单的说一下,若是你们想要跟踪更详细的内存是怎样分配的话,能够学着使用下Windows>ShowView>Allocation Tracker工具来定位你的内存何时被什么东西占用了,是bundlea或HashMap或ArrayList焦点或是其它的什么占用了这些均可以在这个插件中查看到的,简单的使用方法就是:选中Device进程列表中的某一个进程,让Allocation Tracker里面的跟踪工具处于可用状态,点击Stop Tracking来跟踪程序,过必定时间后,点击Get Allocations来获取内存分配的消息就能够查看更为详细的内存分配状况了,以下图:**



** Allocation Tracker这个小工具比较简单, 在这个地方不一一说明了,想学的朋友们本身点击看看就会知道里面的参数是什么意思了….._……..****最后,仍是同样,在内存管理方面还要学的东西不少,若是文章中有讲的不清楚或不足之处,你们留言批评并提出更好的建议,必定及时改进,在此先谢谢你们啦,吼吼学习学习!你们加油,一块儿进步!!!

相关文章
相关标签/搜索