大家有或经常碰到OOM的问题,对吧?很多这样的问题只要一出现相信大家的想法跟小马的一样,就是自己的应用:优化、优化、再优化!而且如果出现类似于OOM这样级别的问题,根本就不好处理,LogCat日志中显示的信息仅仅是OOM,并不会给你提示如何解决的方法或思路,因为引起OOM的原因是你应用的问题,不是系统问题!应该想下,在优化之前找到需要优化的地方,再去做优化操作不是更直接吗?相信大多数朋友应该经常听过或使用Jnuit调试吧,好了,废话不多说,今天小马就跟大家一起来学习总结下OOM的调试方法,来找到需要优化的地方,要知道OOM也是可以一步步调试的:

首先,先一起来做些小小的知识铺垫:

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

           1. 查询数据库没有关闭游标

              程序中经常会进行查询数据库的操作,但是经常会有使用完毕Cursor后没有关闭的情况。如果我们的查询结果集比较小,对内存的消耗不容易被发现,只有在常时间大量操作的情况下才会复现内存问题,这样就会给以后的测试和问题排查带来困难和风险示例代如下码:

  1. Cursor cursor = getContentResolver().query(uri ...);
  2. if (cursor.moveToNext()) {
  3. ... ...
  4. }
  5. 修正示例代码:
  6. Cursor cursor = null;
  7. try {
  8. cursor = getContentResolver().query(uri ...);
  9. if (cursor != null && cursor.moveToNext()) {
  10. ... ...
  11. }
  12. } finally {
  13. if (cursor != null) {
  14. try {
  15. cursor.close();
  16. } catch (Exception e) {
  17. //ignore this
  18. }
  19. }
  20. }

 2. 构造Adapter时,没有使用缓存的 convertView ,这个问题小马上一篇:ListView加载数据原理及优化总结(二十一)中已经讲过了,大家可以回过头看下

                3. 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。以后遇到这样的异常抛出时就要知道怎么回事咯!

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

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

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

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

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

 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为应用进程分配的内存上限如下所示:(下面这些是小马网上查到的,小马不懂下面的语法,但知道有限制这么一回事就够了,此处不研究下面的代码)
位置: /ANDROID_SOURCE/system/core/rootdir/init.rc 部分脚本
# Define the oom_adj values for the classes of processes that can be
# 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

 d) 此处以com.xiaoma.www进程为例,在我的测试环境中com.xiaoma.www进程所占用的内存的data object的Total Size正常情况下稳定在0.8~1.0M之间,而当其值超过3~5M每次启动应用该值不稳定的时候进程就会被系统杀掉啦!

方式二:

内存监测工具 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这个小工具比较简单, 小马在这个地方不一一说明了,想学的朋友们自己点击看看就会知道里面的参数是什么意思了…..^_^……..最后,还是一样,小马在内存管理方面还要学的东西很多,如果文章中小马有讲的不清楚或不足之处,大家留言批评并提出更好的建议,小马一定及时改进,在此先谢谢大家啦,吼吼学习学习!大家加油,一起进步!!!

本文出自 “酷_莫名简单、KNothing” 博客,请务必保留此出处http://mzh3344258.blog.51cto.com/1823534/895093

【转】Android之内存泄漏调试学习与总结的更多相关文章

  1. android性能测试内存泄漏

    1.什么是内存泄漏?     适用于该系统的内存使用内存泄漏,未回复(释放),该内存可以没有事业,也不能被其他人使用使用自己. 2.出有什么差别?    内存泄漏是分配出去的内存无法回收.    内存 ...

  2. [轉]Android的内存泄漏和调试

    一. Android的内存机制 Android的程序由Java语言编写,所以Android的内存管理与Java的内存管理相似.程序员通过new为对象分配内存,所有对象在java堆内分配空间:然而对象的 ...

  3. python 内存泄漏调试

    Python应用程序内存泄漏的调试 Quake Lee quakelee@geekcn.org 新浪网技术(中国)有限公司 Sina Research & Development Python ...

  4. 利用Android Studio、MAT对Android进行内存泄漏检测

    利用Android Studio.MAT对Android进行内存泄漏检测 Android开发中难免会遇到各种内存泄漏,如果不及时发现处理,会导致出现内存越用越大,可能会因为内存泄漏导致出现各种奇怪的c ...

  5. Android防止内存泄漏以及MAT的使用

    Android发生内存泄漏最普遍的一种情况就是长期保持对Context,特别是Activity的引用,使得Activity无法被销毁.这也就意味着Activity中所有的成员变量也没办法销毁.本文仅介 ...

  6. android 常见内存泄漏原因及解决办法

    android常见内存泄漏主要有以下几类: 一.Handler 引起的内存泄漏. 在Android开发中,我们经常会使用Handler来控制主线程UI程序的界面变化,使用非常简单方便,但是稍不注意,很 ...

  7. [原理] Android Native内存泄漏检测原理解析

    转载请注明出处:https://www.cnblogs.com/zzcperf/articles/11615655.html 上一篇文章列举了不同版本Android OS内存泄漏的检测操作(传送门), ...

  8. Android应用内存泄漏的定位、分析与解决策略

    什么是内存泄漏 对于不同的语言平台来说,进行标记回收内存的算法是不一样的,像 Android(Java)则采用 GC-Root 的标记回收算法.下面这张图就展示了 Android 内存的回收管理策略( ...

  9. 每日一问:Android 中内存泄漏都有哪些注意点?

    内存泄漏对每一位 Android 开发一定是司空见惯,大家或多或少都肯定有些许接触.大家都知道,每一个手机都有一定的承载上限,多处的内存泄漏堆积一定会堆积如山,最终出现内存爆炸 OOM. 而这,也是极 ...

随机推荐

  1. 剑指offer--面试题23

    //该题原本想用递归实现,但却用循环实现了... //关键用了个队列 #include <queue> void Print(BinaryTreeNode* pRoot, std::que ...

  2. Poj 2349 Arctic Network 分类: Brush Mode 2014-07-20 09:31 93人阅读 评论(0) 收藏

    Arctic Network Time Limit: 2000MS   Memory Limit: 65536K Total Submissions: 9557   Accepted: 3187 De ...

  3. 在linux中使用phpize安装php扩展模块

    介绍:linux系统中,php安装成功后,在bin目录下会生成一个名叫phpize的可执行脚本,这个脚本的用途是动态安装php扩展模块.使用phpize脚本安装php扩展模块的好处:在安装php时没有 ...

  4. VC++之GetLastError()使用说明

    VC中GetLastError()获取错误信息的使用 在VC中编写应用程序时,经常需要涉及到错误处理问题.许多函数调用只用TRUE和FALSE来表明函数的运行结果.一旦出现错误,MSDN中往往会指出请 ...

  5. UML状态图(转载)

    概述: 图表本身的名称,阐明该图的目的和其他细节.它描述了在一个系统中的一个组成部分不同的状态.状态是特定的一个系统的组件/对象. 状态图描述了一个状态机.我们阐明的状态机可以被定义为一台机器,它定义 ...

  6. SQL Server 2008 R2评估期已过的解决办法

    SQL Server 2008 R2评估期已过的解决办法   发现问题 北美产品测试服每日随机任务没有刷新 每日随机任务是使用数据库作业定期执行操作,重置玩家随机任务项 排查问题  www.2cto. ...

  7. javascript实现数据结构与算法系列:功能完整的线性链表

    由于链表在空间的合理利用上和插入,删除时不需要移动等的有点,因此在很多场合下,它是线性表的首选存储结构.然而,它也存在着实现某些基本操作,如求线性表长度时不如顺序存储结构的缺点:另一方面,由于在链表中 ...

  8. 从3D Studio Max导入物体 Importing Objects From 3D Studio Max

    原地址:http://game.ceeger.com/Manual/HOWTO-ImportObjectMax.html If you make your 3D objects in 3dsMax, ...

  9. mysql同时修改2个表思路

    1.需求:修改评论表中的昵称为手机号码最后4位. ,) AND issuer_name LIKE '1%'; 2.由于误操作(MID(issuer_name,4,6)是中间的6位),需要数据回滚. 3 ...

  10. 由浅入深了解Thrift之客户端连接池化续

    前文<由浅入深了解Thrift之客户端连接池化>中我们已经实现了服务调用端 连接的池化,实现的过于简陋,离实际的项目运用还很遥远.本文将在进一步改造,主要是两方面:1.服务端如何注册多个服 ...