Javaandroid 内存败露调试

一、概述 1

② 、Android(Java)中普遍的简单引起内存泄漏的不良代码 1

(一) 查询数据库没有停歇游标 2

(二) 构造Adapter时,没有运用缓存的 convertView 3

(三) Bitmap对象不在使用时调用recycle()释放内存 4

(四) 释放对象的引用 4

(五) 其他 5

叁 、内存监测工具 DDMS –> Heap 5

肆 、内存分析工具 MAT(Memory Analyzer Tool) 7

(一) 生成.hprof文件 7

(二) 使用MAT导入.hprof文件 8

(三) 使用MAT的视图工具分析内存 8

 

 

一、概述

    Java编程中时常简单被忽视,但自己又十二分重大的一个标题就是内存使用的难题。Android应用主要采取Java语言编写,由此那么些题材也一样会在Android开发中出现。本文不对Java编程难点做探索,而是对于在Android中,越发是采取开发中的此类难题开展整治。

    由于我接触Android时间并不是十分长,因而如有叙述不当之处,欢迎指正。

② 、Android(Java)中广大的不难招惹内存泄漏的糟糕代码

    Android首要利用在嵌入式设备当中,而嵌入式设备由于有的肯定的标准化限制,平常都不会有很高的配置,越发是内存是相比单薄的。假设大家编辑的代码当中有太多的对内存使用不当的地点,难免会使得大家的配备运转缓慢,甚至是死机。为了可以使得Android应用程序安全且高效的周转,Android的每一个应用程序都会使用三个专有的Dalvik虚拟机实例来运作,它是由Zygote服务进程孵化出来的,也等于说每一个应用程序都以在属于自个儿的历程中运作的。一方面,借使程序在运作进程中出现了内存泄漏的题材,仅仅会使得本身的进度被kill掉,而不会潜移默化此外进程(纵然是system_process等系统经过出难点来说,则会引起系统重启)。另一方面Android为差距品种的经过分配了差其余内存使用上限,如若运用进度使用的内存当先了那一个上限,则会被系统就是内存泄漏,从而被kill掉。Android为利用进度分配的内存上限如下所示:

位置: /ANDROID_SOU福特ExplorerCE/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

 

    正因为大家的应用程序可以使用的内存有限,所以在编写代码的时候须求越发注意内存使用难题。如下是一些广阔的内存使用不当的图景。

 

(一) 查询数据库没有关闭游标

描述:

    程序中不时会展开查询数据库的操作,不过日常会有使用达成Cursor后不曾停歇的场合。假如大家的查询结果集相比小,对内存的损耗不便于被察觉,唯有在常时间多量操作的景观下才会复现内存难点,这样就会给未来的测试和题材排查带来不方便和高危害。

 

演示代码:

Cursor cursor = getContentResolver().query(uri …);

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

        }

    }

 

(二) 构造Adapter时,没有动用缓存的 convertView

描述:

    以结构ListView的BaseAdapter为例,在BaseAdapter中提升了章程:

public View getView(int position, View convertView, ViewGroup parent)

来向ListView提供每三个item所需求的view对象。开始时ListView会从BaseAdapter中按照如今的显示屏布局实例化一定数量的view对象,同时ListView会将那么些view对象缓存起来。当发展滚动ListView时,原先位于最上边的list item的view对象会被回收,然后被用来协会新现身的最下边的list item。这么些结构进度就是由getView()方法成功的,getView()的第二个形参 View convertView就是被缓存起来的list item的view对象(开首化时缓存中没有view对象则convertView是null)。

    由此可以观察,倘若大家不去接纳convertView,而是每一次都在getView()中重复实例化二个View对象的话,即浪费能源也浪费时间,也会使得内存占用越来越大。ListView回收list item的view对象的进程可以查阅:

android.widget.AbsListView.java –> void addScrapView(View scrap) 方法。

 

演示代码:

public View getView(int position, View convertView, ViewGroup parent) {

    View view = new Xxx(…);

    … …

    return view;

}

 

校勘示例代码:

public View getView(int position, View convertView, ViewGroup parent) {

    View view = null;

    if (convertView != null) {

        view = convertView;

        populate(view, getItem(position));

        …

    } else {

        view = new Xxx(…);

        …

    }

    return view;

 

(三) Bitmap对象不在使用时调用recycle()释放内存

描述:

    有时我们会手工的操作Bitmap对象,若是2个Bitmap对象相比较占内存,当它不在被应用的时候,可以调用Bitmap.recycle()方法回收此目的的像素所占有的内存,但那不是必须的,视景况而定。可以看一下代码中的注释:

    /**

     * Free up the memory associated with this bitmap’s pixels, and mark the

     * bitmap as “dead”, meaning it will throw an exception if getPixels() or

     * setPixels() is called, and will draw nothing. This operation cannot be

     * reversed, so it should only be called if you are sure there are no

     * further uses for the bitmap. This is an advanced call, and normally need

     * not be called, since the normal GC process will free up this memory when

     * there are no more references to this bitmap.

     */

(四) 释放对象的引用

描述:

    那种情景描述起来相比较费心,举两个例证举办求证。

示例A:

即使有如下操作

public class DemoActivity extends Activity {

    … …

    private Handler mHandler = …

    private Object obj;

    public void operation() {

     obj = initObj();

     …

     [Mark]

     mHandler.post(new Runnable() {

            public void run() {

             useObj(obj);

            }

     });

    }

}

    大家有壹个分子变量 obj,在operation()中大家期望可以将拍卖obj实例的操作post到有个别线程的MessageQueue中。在以上的代码中,尽管是mHandler所在的线程使用完了obj所引述的对象,但那些目的如故不会被垃圾回收掉,因为DemoActivity.obj还存有那些目的的引用。所以即使在德姆oActivity中不再行使这几个目标了,可以在[Mark]的地方释放对象的引用,而代码可以修改为:

… …

public void operation() {

    obj = initObj();

    …

    final Object o = obj;

    obj = null;

    mHandler.post(new Runnable() {

        public void run() {

            useObj(o);

        }

    }

}

… …

 

示例B:

    借使我们期望在锁屏界面(LockScreen)中,监听系统中的电话服务以取得一些音信(如信号强度等),则可以在LockScreen中定义二个PhoneStateListener的靶子,同时将它注册到TelephonyManager服务中。对于LockScreen对象,当必要出示锁屏界面的时候就会创设3个LockScreen对象,而当锁屏界面消失的时候LockScreen对象就会被放出掉。

    不过只要在出狱LockScreen对象的时候忘记打消大家事先注册的PhoneStateListener对象,则会造成LockScreen无法被垃圾回收。假诺持续的使锁屏界面呈现和消逝,则最后会出于大气的LockScreen对象没有章程被回收而引起OutOfMemory,使得system_process进度挂掉。

    综上说述当一个生命周期较短的对象A,被3个生命周期较长的目的B保有其引用的事态下,在A的生命周期为止时,要在B中消除掉对A的引用。

(五) 其他

    Android应用程序中最典型的内需小心释放能源的景观是在Activity的生命周期中,在onPause()、onStop()、onDestroy()方法中必要适宜的放出财富的情状。由于此情状很基础,在此不详细表明,具体可以查看官方文档对Activity生命周期的牵线,以明显曾几何时应该释放怎么着能源。

http://rayleeya.iteye.com/blog/727074

相关文章