Android 之内存泄漏调试学习与总结,kotlin 入门书籍推荐
}修正示例代码: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}}}
构造 Adapter 时,没有使用缓存的 convertView
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 为应用进程分配的内存上限如下所示:(下面这些是网上查到的,不懂下面的语法,但知道有限制这么一回事就够了,此处不研究下面的代码)
killed by the kernel. These are used in ActivityManagerServicesetprop ro.FOREGROUND_APP_ADJ 0setprop ro.VISIBLE_APP_ADJ 1setprop ro.SECONDARY_SERVER_ADJ 2setprop ro.BACKUP_APP_ADJ 2setprop ro.HOME_APP_ADJ 4setprop ro.HIDDEN_APP_MIN_ADJ 7setprop ro.CONTENT_PROVIDER_ADJ 14setprop ro.EMPTY_APP_ADJ 15Define the memory thresholds at which the above process classes willbe killed. These numbers are in pages (4k)setprop ro.FOREGROUND_APP_MEM 1536setprop ro.VISIBLE_APP_MEM 2048setprop ro.SECONDARY_SERVER_MEM 4096setprop ro.BACKUP_APP_MEM 4096setprop ro.HOME_APP_MEM 4096setprop ro.HIDDEN_APP_MEM 5120setprop ro.CONTENT_PROVIDER_MEM 5632setprop ro.EMPTY_APP_MEM 6144
最后
目前已经更新的部分资料:
《Android 学习笔记总结+最新移动架构视频+大厂安卓面试真题+项目实战源码讲义》
【docs.qq.com/doc/DSkNLaERkbnFoS0ZF】 完整内容开源分享
)


评论