• Android内存泄漏分析实战


    内存泄漏简单介绍

    java能够保证当没有引用指向对象的时候,对象会被垃圾回收器回收。与c语言自己申请的内存自己释放相比,java程序猿轻松了非常多。可是并不代表java程序猿不用操心内存泄漏。当java程序发生内存泄漏的时候往往具有隐蔽性。因此要借助一些专业的平台资源去保证安全性,比如能够通过加密实现

    定义

    引用百度百科的定义:“用动态存储分配函数动态开辟的空间,在使用完成后未释放,结果导致一直占领该内存单元。

    直到程序结束”。从程序员的角度来看“内存泄漏”,事实上就是一个对象的生命周期超出了程序员所预期的长度(就叫它“该死不死”吧!

    ),那么这个对象就泄漏了。

    android开发中的内存泄漏

    android应用程序本身系统分配的内存非常少,一旦发生泄漏,程序非常快就会变得非常卡顿,直至OOM崩溃。接下来将通过一个案例(仅仅是为了分析内存泄漏而设计的玩具程序。切勿模仿)来介绍内存泄漏分析工具MAT。以及内存分析的技巧。

    公欲善其事。先利其器

    准备内存泄漏的分析工具,能够安装eclipse插件mat。假设eclise安装mat不成功,那可能是缺少必要的libs。假设嫌找库麻烦,能够仅仅勾选第二项安装,只是会缺少某些功能,可是也够用了。 
    在线安装:http://download.eclipse.org/mat/1.4/update-site/ 
    下载安装:http://mirror.hust.edu.cn/eclipse//mat/1.4/MemoryAnalyzer-1.4.0.201406041413.zip

    mat插件怎样使用

    假设已经安装成功好了mat工具,使用起来很easy,首先将须要分析的应用程序跑起来,打开eclipse的devices视图你将会看到点击“Dump Hprof file”button,注意点击一下就能够了,然后等待(等待几秒)dump一个内存快照出来,接下来就会自己主动打开mat的视图了,假设mat没有安装成功,会让你保存一个.hprof文件到本地。

    看看以下的图例吧

    dump hprof启动mat工具

    人为制造一个内存泄漏

    自己定义一个ActivityManager。提供两个方法,分别用来注冊与反注冊Activity。源代码下载

    public class ActivityManager {
        private List<Activity> mActivities = new ArrayList<>();
        private static ActivityManager sInstance;
    
        private ActivityManager() {
        };
    
        public static ActivityManager instance() {
            if (sInstance == null) {
                sInstance = new ActivityManager();
            }
    
            return sInstance;
        }
    
        public void registActivity(Activity activity) {
            mActivities.add(activity);
        }
    
        public void unRigistActivity(Activity activity) {
            mActivities.remove(activity);
        }
    }

    在MainActivity的onCreate与onDestroy中分别调用registActivity和registActivity方法进行注冊与反注冊。可是OtherActivity却仅仅是注冊了。而忘记了反注冊。

    public class MainActivity extends Activity {
        public static final String TAG = MainActivity.class.getSimpleName();
    
        private Button mBtn;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            mBtn = (Button) findViewById(R.id.btn);
            mBtn.setOnClickListener(new OnClickListener() {
    
                @Override
                public void onClick(View v) {
                    Intent intent = new Intent();
                    intent.setClass(MainActivity.this, OtherActivity.class);
                    startActivity(intent);
                }
            });
            
            ActivityManager.instance().registActivity(this);
        }
    
        @Override
        protected void onDestroy() {
            super.onDestroy();
            
            ActivityManager.instance().registActivity(this);
        }
    
    public class OtherActivity extends Activity {
        public static final String TAG = OtherActivity.class.getSimpleName();
    
        private Object[] mObjs = new Object[10000];//模拟高速消耗内存。使效果明显
        private Button mBtn;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_other);
            mBtn = (Button) findViewById(R.id.btn);
            mBtn.setOnClickListener(new OnClickListener() {
    
                @Override
                public void onClick(View v) {
                    for (int i = 0; i < mObjs.length; i++) {
                        mObjs[i] = new Object();
                    }
                    
                    finish();
                }
            });
    
            ActivityManager.instance().registActivity(this);
        }
    
        @Override
        protected void onDestroy() {
            super.onDestroy();
        }
    }

    案例中的内存泄漏是人为构造的。所以我们事先已经知道有泄漏了,可是实际的开发过程中。内存泄漏是隐蔽的,一開始我们并不知道,所以我们须要通过一些手段来測试APP是否有内存泄漏。首先在Devices视图中选中须要測试的进程。然后点击Device视图面板的Update Heapbutton。然后打开Heap视图,点击Cause GC。然后重复的在MainActivyt和OtherActivity之间切换。观察Heap size的变化。你会发现内存一直在添加。没有稳定下来的趋势。

    这个时候你就有理由怀疑内存泄漏了。

    Update heap观察heap size等变化情况

    找出泄漏的对象

    依照前面mat的使用步骤,dump一个内存快照出来。然后从分析报告中点击“Leak suspects”这里会列车可能泄漏的对象,当中你会发现“ com.vjson.leaks.OtherActivity”的身影。OtherActivity这个类有33个实例。作为代码的生产者,你应该一下子就会发现,原来是OtherActivity泄漏了。

    发现它泄漏之后,怎样找出是哪一个对象持有了OtherActivity对象的引用呢?

    可能泄漏的报告

    找出引用链

    使用OQL对象查询语言查询出泄漏的对象,写过SQL的同学一定对她有一种既陌生又熟悉的感觉。和SQL很相似,语法简单易懂,可是很强大select *from com.vjson.leaks.OtherActivity赛选出OtherActivity这一类对象,然后选择“exclude weak/soft references”赛选出除了软引用和弱引用之外的对象,也就是强引用了!。对象的引用类型不在本文的解说范围,可是你一定要知道“强引用”,“软引用”。“弱引用”。“幽灵引用”,假设不知道自行脑补去吧!

    OQL对象查询找出引用链

    对象引用链

    然后找出GC的根节点,从图二种能够看出,原来Activity对象被ActivityManager里面的ArrayList给hold住了,所以接下来的工作就是在OtherActivity的onDestroy中反注冊,内存泄漏就被攻克了。

    Android开发中常见的内存泄漏

    对象没有反注冊

    数据库cursor没有关闭

    Bitmap没有回收

    ListView item没有复用

    Handler在Activity中定义为非static的匿名内部类

    总结

    假设耐心的看完本文。那么恭喜你妈妈再也不用操心内存泄漏了。事实上仅仅要掌握了分析问题的技巧与工具,内存泄漏so easy。文章中仅仅是简单的介绍了工具与技巧。这当中还有非常多技巧须要自己去摸索。

     

  • 相关阅读:
    STL常用容器☞String容器
    初识STL
    函数模板
    多态
    运算符重载
    友元
    对象的初始化和清理
    C++内存分区模型
    传值和传地址
    const的使用
  • 原文地址:https://www.cnblogs.com/yfceshi/p/6767506.html
Copyright © 2020-2023  润新知