Memory Leak

android中,jvm具有自动回收的机制,会不定时不定期的去清理无用的被占用的内存,

而在理论上不需要再被使用的内存,在实际中却还持有对这一块内存的引用,导致GC时,不会被回收释放掉,这部分内存就会随着程序的运行不断堆积,从而导致应用分配的内存不够使用导致卡顿、ANR异常等情况。

一般内存泄漏(traditional memory leak):由忘记释放分配的内存导致的(Cursor忘记关闭等)
逻辑内存泄漏(logical memory leak):当应用不再需要这个对象,但仍未释放该对象的所有引用

 

一、 命令

1.  meminfo dump

adb shell dumpsys meminfo com.xxx.xxx.xxx

2.  android hprof dump

adb shell am dumpheap com.xxx.xxx.xxx   /data/local/tmp/meminfo.hprof
adb pull data/local/tmp/meminfo.hprof  /d/meninfo/

3. 使用 mat 工具查看

hprof-conv  xxxx.hprof    xxxxx_mat.hprof

二、


强引用(StrongReference):JVM宁可抛出OOM,也不会让GC回收具有强引用的对象           生命周期:JVM停止的时候才会终止
软引用(SoftReference):只有在内存空间不足时,才会被回收的对象                     生命周期:内存不足时终止
弱引用(WeakReference):在GC时,一旦发现了只具有弱引用的对象,不管当前内存空间足够与否,都会回收它的内存
虚引用(PhantomReference):任何时候都可以被GC回收,当垃圾回收器准备回收一个对象时,如果发现它还有虚引用,就会在回收对象的内存之前,把这个虚引用加入到与之关联的引用队列中。程序可以通过判断引用队列中是否存在该对象的引用,来了解这个对象是否将要被回收。可以用来作为GC回收Object的标志。                  生命周期:GC后终止
 

成员变量全部存储在堆中(包括基本数据类型,引用及引用的对象实体) ---- 属于类,类对象  AA aa = new AA();

局部变量的基本数据类型和引用存储于栈当中。--因为他们属于方法当中的变量,生命周期会随着方法一起结束

 

三、

3.1  Activity 泄漏 

       (1) Activity 被静态方法引用 无法释放  :   Settings.getXX(mActivity);
       重点:如果持有Activity对象的强引用,垃圾回收器是无法在内存中回收这个对象

       (2) Activity  静态变量 

       Singleton的getInstance()方法时传入了Activity  , static Activity activity;        
       如果这个静态变量在activity生命结束后没有清空,就会导致内存泄漏。因为static贯穿了这个应用的生命周期
       所以被泄露的Activity就会一直存在于应用的进程中,不会被垃圾回收器回收。 
       
       最容易引发的内存泄漏为 Context,如Activity的Context,就包含了大量的内存引用(View Hierarchies和其他资源)
       重要:一旦泄漏Context,就意味着泄漏它指向的所有对象。
       如果Activity.onDestroy()执行完,堆栈中仍存在持有该activity的引用,垃圾回收器就无法把他标记成已回收的内存(结果就是Activity存活在它的生命周期之外)

       (3) Static Views      单例中保存activity

        在单例中,如果Activity经常被用到,那么内存中保存一个实例是很实用的。但由于单例的生命周期是应用程序的生命周期,
    这样做会延长Activity的生命周期(强制延长Activity的生命周期是相当危险且不必要的,无论如何都不能在单例中保存类似的Activity对象)

        PS: 如果一个View初始化耗费大量资源,而且在一个activity生命周期内保持不变,那可以把它变成static,加载到视图树上(View Hierachy)
    像这样,当activity被销毁时,应当释放资源。(在销毁视图时应该把这个static view的view置为null)

       PS:导致潜在的内存泄漏的两种:
       全局进程(process-global)的static变量。这个无视应用状态,持有activity的强引用的东西
       活在Activity生命周期之外的线程。没有清空对Activity的强引用。
 

       

     静态的类对象会一直存在,并且他持有Activity 的引用(内部类),导致Activity无法释放.
     内部类的优势之一就是可以访问外部类,不好的是,导致内存泄漏的原因,就是因为内部类持有外部类实例的引用。
     因为内部类的一个特性是它们可以访问外部类的变量,所以它们必然持有了对外部类实例的引用以至于 Activity 会发生泄漏。

     解决方案:销毁的时候 InnerClass = null
       Activity{
           private static InnerClass in;
	   onCreate(){
		in = new InnerClass();  
	   }
           
	   class InnerClass {
           
	   }
	}
      
      解决方案:销毁的时候 view = null
       Activity{
           static view;
           void initView() {
               view = this.findViewById(R.id.sv_button);
           }
       }

 

3.2 内部类   Inner Classes,  匿名类  Anonymous Classes

优点: 提高可读性,但是导致内存泄漏的原因,就是内部持有外部类实例的强引用,如内部类中持有Activity对象

静态内部类解决内存泄漏
非静态内部类导致内存泄漏主要原因:App大量的内存泄漏导致内存耗尽,会由于内存空间不足,出现频繁的GC,每一次GC都是一个耗时阻塞操作,会造成设备卡顿。
非静态内部类中创建了一个静态实例,导致该实例的生命周期和应用ClassLoader级别,又因为该静态实例会隐式持有其外部类的引用,所以导致其外部类无法正常释放,出现泄漏问题。


Android开发中,最容易引发的内存泄漏问题的是Context。比如Activity的Context,就包含大量的内存引用,例如View Hierarchies和其他资源。
一旦泄漏了Context,也意味泄漏它指向的所有对象。Android机器内存有限,太多的内存泄漏容易导致OOM。


1.非静态内部类会对外部类存在一个隐式引用            非静态(匿名)内部类会持有外部类的引用,静态内部类中未持有外部类的引用。

2.非静态内部类中存在异步任务,可能导致其对应的外部类内存资源无法正常释放        

3.非静态内部类中创建了一个静态实例,会导致内存泄漏


解决办法           1.将内部类变为静态内部类,把匿名内部类变为静态匿名内部类 (static 修饰的不受类的限制)

                          2.如果有强引用Activity中的属性,则将该属性的引用方式改为弱引用

                          3.在业务运行的情况下,当Activity执行onDestroy时,结束这些耗时任务(取消强引用)

       1. 匿名的Runnable,用匿名Handler执行。Runnable内部类会持有外部类的隐式引用,被传递到Handler的消息队列MessageQueue中,
	 在Message消息没有被处理之前,Activity实例不会被销毁,于是导致了内存泄漏。
	 Activity{
	    mHanlder.postDelayed(new Runnable() {
		@Override public void run() {
		    
		}
	    });
	 }

        2. Thread 匿名类的实例,不管是不是在工作线程,都会持有Activity的引用,导致内存泄漏
	 Activity{
	     new Thread() {
	        @Override public void run() {
	 	 
	      }
	     }.start();
	  }

        3. AsyncTask匿名类的实例,会持有Activity的引用,导致内存泄漏
        Activity{
          void startAsyncTask() {
                new AsyncTask<Void, Void, Void>() {
                }
          }.execute();
         }

弱引用WeakReference(弱引用),它能保证在系统调用GC时就会被系统释放资源

    
总结: 不是所有内部类只能使用静态内部类,只有在该内部类中的生命周期不可控的情况下,采用静态内部类。
解决思路:去掉隐式引用(静态(匿名)内部类),手动管理对象引用(修改静态内部类的构造方法,手动引入其外部类引用)
          当内存不可用时,不执行不可控代码(Android 可以结合智能指针 ,WeakReference包裹外部类实例)

 

   private static class MainHandler extends Handler {

        private final WeakReference<Activity> mActivity;

        public MainHandler(Activity activity) {
            mActivity = new WeakReference<Activity>(activity);
        }

	   public void clear(){
            if(null != mActivity) {
                mActivity.clear();
            }
        }

        @Override
        public void handleMessage(Message msg) {
            // TODO Auto-generated method stub
            Activity activity = (MainActivity) reference.get();
         }
      };

   MainHandler mHandler = new MainHandler(this);

     @Override
     protected void onDestroy() {
        super.onDestroy();
        FixedThreadPoolManager.release();
        mHandler.clear();
        mHandler.removeCallbacksAndMessages(null);
}

 

3.3 服务注册类泄漏

 
   服务注册类泄漏

   通过Context.getSystemService(int name) 可获取系统服务。这些服务工作在各自的进程中,这会导致服务持有了Context的引用,
   如果在Activity销毁的时候没有注销这些监听,会导致内存泄漏

 

 

 

 

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

空白的泡

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值