目录
1)内存相关的五种常见问题
 2)内存溢出和内存泄漏
 3)LeakCanary是什么?
 4)LeakCanary如何使用,如何分析?
 5)LeakCanary监测的内容
提问:程序有时候很卡,经常会出现闪退,出现程序无响应,在项目过程中遇到主要的问题我们应该如何解决呢?这篇文章会介绍LeakCanary工具来分析和解决这些问题。
一、内存相关的五种常见问题
OOM(Out of Memory)是指内存不足,即应用程序在运行过程中申请的内存超过了系统可用的内存资源。当系统检测到内存不足时,会触发 OOM 错误并终止应用程序的运行。
OOM 通常发生在应用程序加载大量图片、缓存数据、创建大对象等场景下。如果应用程序没有有效地管理内存,持续申请内存而不释放,就容易导致内存溢出,最终触发 OOM 错误。
 为什么明明还有4M的内存空间,加载1M的图片是,会报OOM呢,因为内存空间不连续。
为什么明明还有4M的内存空间,加载1M的图片是,会报OOM呢,因为内存空间不连续。
 
二、内存溢出和内存泄漏
内存溢出指的是应用程序在运行过程中申请的内存超过了系统可用的内存资源。当系统检测到内不足时,会触发 Out of Memory(OOM)错误并终止应用程序的运行。内存溢出通常是由于应用程序持续申请内存而没有及释放导致的,最终导致系统无法为应用程序提供足够的内存空间。
内存泄漏指的是应用程序在使用完某些对象后,没有正确地释放对这些对象的引用导致这些对象无法被垃圾回收器回收,从而占用了系统的内存资源。随着时间的推移,内存泄漏累积起来,最终导致可用内存逐渐减少,可能触发 OOM 错误或导致应用程序变得缓慢、不稳定。
80%的原因都是因为内存泄漏导致内存溢出。
2.1 常见的内存泄漏
- 单例造成的内存泄露
- 非静态内部类创建静态实例造成的內存泄露
- Handler造成的内存泄露
- 线程造成的内存泄露
(1)为什么单例会造成内存泄漏?

(2)非静态内部类创建静态实例造成的內存泄露
public class OuterClass {
    static SomeListener listener;
    public void createLeak() {
        // 创建非静态内部类的实例并将其赋值给静态变量
        listener = new InnerClass();
    }
    private class InnerClass implements SomeListener {
        // 实现一些监听器的方法
    }
}
OuterClass 是外部类,InnerClass是非静态内部类。当调用createLeak()方法时,会创建InnerClass的实例并将其赋值给静态变量listener。由于 InnerClass是非静态内部类,它隐式地持有对外部类OuterClass引用。因此,即使 createLeak() 方法执行完毕后,InnerClass` 的实例仍然存在,并且无法被垃圾回收,导致内存泄漏。
为了解决这个问题,内部类改为静态内部类:将 InnerClass 改为静态内部类,这样它就不再隐式地持有外部类的引用
public class OuterClass {
    private static SomeListener listener;
    public void createLeak() {
 // 创建静态内部类的实例并将其赋值给静态变量
        listener = new InnerClass();
    }
    private static class InnerClass implements SomeListener {
 // 实现一些监听器的方法
    }
}
(3)Handler造成的内存泄漏
public class MainActivity extends AppCompatActivity {
    private static final int_CODE = 1;
    private Handler mHandler;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        // 创建 Handler 实例,并在 handleMessage() 方法中
        mHandler = new Handler() {
            @Override
            public void handleMessage(Message msg) {
                if (msg.what == MESSAGE_CODE) {
 // 处理消息。这里可能会发生内存泄漏,因为发送的延迟消息。
                }
            }
        };
        // 发送延迟消息
        mHandler.sendEmptyMessageDelayed(MESSAGE_CODE, 1000);
    }
    @Override
    protected void onDestroy() {
        super.onDestroy();
        // 方法一:移除所有未处理的消息和回调,避免内存泄漏
        mHandler.removeCallbacksAndMessages(null);
    }
}
方法二
可以使用弱引用来避免Handler持有对外部类的引用
import android.os.Handler;
import android.os.Message;
import java.lang.ref.WeakReference;
public class MyActivity extends Activity {
    private static class MyHandler extends Handler {
        private WeakReference<MyActivity> mActivityRef;
        public MyHandler(MyActivity activity) {
            mActivityRef = new WeakReference<>(activity);
        }
        @Override
        public void handleMessage(Message msg) {
            MyActivity activity = mActivityRef.get();
            if (activity != null) {
                // 处理消息
            }
        }
    }
    private MyHandler mHandler;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mHandler = new MyHandler(this);
        // 发送延迟消息
        mHandler.sendEmptyMessageDelayed(1, 1000);
    }
    @Override
    protected void onDestroy() {
        super.onDestroy();
        // 移除所有未处理的消息和回调
        mHandler.removeCallbacksAndMessages(null);
    }
}
(4)线程
public class MainActivity extends AppCompatActivity {
    private Thread mThread;
    @Override    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        // 创建一个线程并启动
        mThread = new Thread(new Runnable() {
            @Override
            public void run() {
                // 模拟耗时操作
                try {
                    Thread.sleep(5000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        mThread.start();
    }
    @Override
    protected void onDestroy() {
        super.onDestroy();
        // 销毁Activity时,没有正确停止线程,导致线程持有Activity的引用而无法被回收,造成内存泄露
    }
}
public class MainActivity extends AppCompatActivity {
    private Handler mHandler;
    private Runnable mRunnable;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mHandler = new Handler();
        mRunnable = new Runnable() {
            @Override
            public void run() {
                // 模拟耗时操作
                try {
                    Thread.sleep(5000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        };
        mHandler.post(mRunnable);
    }
    @Override
    protected void onDestroy() {
        super.onDestroy();
        // 停止线程
        mHandler.removeCallbacks(mRunnable);
    }
}
可以看到种类还是比较多,如果我们想完全去避免,还是比较难的,可能一不小心,就会写错了,所以我们要介绍一下今天的主角"LeakCanary"。
三、LeakCanary是什么?
LeakCanary是一个用于检测Android应用中内存泄漏的开源库。它由Square公司开发并维护,旨在帮助开发者及时发现和修复应用中的内存泄漏问题。
四、LeakCanary如何使用,如何分析?
(1)引入依赖
  (2)运行debug
(2)运行debug
- 当出现内存溢出的时候会提示这个。
  
 然后我们就可以根据代码进行分析。
  
为什么没有位置显示呢?如何去分析呢?这个其实可以定位到类,定位到某个变量,可能有的人看到这个信息就会很懵,但是学过我们”内存泄漏的常见几种情况“后,那么我们就可以进行分析优化。
五、LeakCanary监测的内容
LeakCanary是一个用于检Android应用程序中内存泄漏的工具。它能够监测和报告以下内容:
-  活动泄漏:当一个Activity被销毁后,仍然存在对的引用,导致无法被垃圾回收。 
-  Fragment泄漏:当一个Fragment被销毁后,仍然存在对它的引用,导致无法被垃圾回收。 
-  对话框泄漏:当对话框被关闭后,仍然存在对它的引用,导致无法被垃圾回收。 
-  广播接收器泄漏:当一个广播接收器未被正确注销,导致持续接收广播,从而引起内存泄漏。 
-  单例对象泄漏:当一个单例对象中持有对Activity或Fragment的引用,导致无法释放这些对象。 
-  视图泄漏:当一个视图对象被销毁后,仍然存在对它的引用,导致无法被垃圾回收。 
当LeakCanary检测到以上情况时,它会生成一个详细的报告,包含泄漏对象的类型、引用链、及内存泄漏的原因等信息,以帮助开发者定位和修复内存泄漏问题。


















