OOM、ANR问题总结

ANR(Application Not Response)

ANR原因及解决方案:

  1. 主线程进行了耗时操作
  • 在主线程进行网络操作
  • 在主线程进行大量的I/O操作、数据库操作

解决办法:耗时操作放在子线程。

  1. 主线程被阻塞时间过长或死锁(调用thread的join()方法、sleep()方法、wait()方法或者等待线程锁的时候)
    参考:https://blog.youkuaiyun.com/zhangqunshuai/article/details/82455503

解决办法:注意代码逻辑,避免死锁和阻塞的情况。

  1. 其他进程(就是其他程序)占用CPU导致本进程得不到CPU时间片,比如其他进程的频繁读写操作可能会导致这个问题。

解决办法:注意代码逻辑,避免死锁和阻塞的情况。

OOM(Out Of Memory)

OOM原因及解決方案

(参考:https://blog.youkuaiyun.com/lc352778616/article/details/77131975

  1. 加载大图片内存泄漏
    在Android应用中加载Bitmap的操作是需要特别小心处理的,因为Bitmap会消耗很多内存。比如,Galaxy Nexus的照相机能够拍摄2592x1936 pixels (5 MB)的图片。 如果bitmap的图像配置是使用ARGB_8888 (从Android 2.3开始的默认配置) ,那么加载这张照片到内存大约需要19MB(259219364 bytes) 的空间,从而迅速消耗掉该应用的剩余内存空间。
    对于分辨率比我们手机屏幕的分辨率高得多的图片,我们应该加载一个缩小版本的图片,从而避免超出程序的内存限制。下面我们就来看一看,如何对一张大图片进行适当的压缩,让它能够以最佳大小显示的同时,还能防止OOM的出现。
    https://github.com/LRH1993/android_interview/blob/master/android/basis/bitmap.md

  2. 单利模式内存泄漏
    不正确使用单例模式是引起内存泄漏的一个常见问题,单例对象在被初始化后将在JVM的整个生命周期中存在(以静态变量的方式),如果单例对象持有外部对象的引用,那么这个外部对象将不能被JVM正常回收,导致内存泄漏。
    如果需要Context,尽量引用Application,而不用Activity
    注意代码逻辑

  3. Handler内存泄漏
    Handler的生命周期与Activity不一致
    由于Handler属于TLS(Thread Local Storage)变量,生命周期和Activity是不一致的。
    当Android应用启动的时候,会先创建一个UI主线程的Looper对象,Looper实现了一个简单的消息队列,一个一个的处理里面的Message对象。主线程Looper对象在整个应用生命周期中存在。
    当在主线程中初始化Handler时,该Handler和Looper的消息队列关联(没有关联会报错的)。发送到消息队列的Message会引用发送该消息的Handler对象,这样系统可以调用 Handler#handleMessage(Message) 来分发处理该消息。
    只要Handler发送的Message尚未被处理,则该Message及发送它的Handler对象将被线程MessageQueue一直持有。因此这种实现方式一般很难保证跟View或者Activity的生命周期保持一致,故很容易导致无法正确释放。
    handler引用Activity阻止了GC对Acivity的回收
    在Java中,非静态(匿名)内部类会默认隐性引用外部类对象。而静态内部类不会引用外部类对象。
    如果外部类是Activity,则会引起Activity泄露。
    当Activity finish后,延时消息会继续存在主线程消息队列中1分钟,然后处理消息。而该消息引用了Activity的Handler对象,然后这个Handler又引用了这个Activity。这些引用对象会保持到该消息被处理完,这样就导致该Activity对象无法被回收,从而导致了上面说的 Activity泄露。
    错误示例:

private final Handler mHandler = new Handler() { 
	@Override public void handleMessage(Message msg) { // … } 
}; 

解决办法:
方法一:通过程序逻辑来进行保护。
1.在关闭Activity的时候停掉你的后台线程。线程停掉了,就相当于切断了Handler和外部连接的线,Activity自然会在合适的时候被回收。
2.如果你的Handler是被delay的Message持有了引用,那么使用相应的Handler的removeCallbacks()方法,把消息对象从消息队列移除就行了。

方法二:将Handler声明为静态类
PS:在Java 中,非静态的内部类和匿名内部类都会隐式地持有其外部类的引用,静态的内部类不会持有外部类的引用。
静态类不持有外部类的对象,所以你的Activity可以随意被回收。由于Handler不再持有外部类对象的引用,导致程序不允许你在Handler中操作Activity中的对象了。所以你需要在Handler中增加一个对Activity的弱引用(WeakReference)

正确示例:

static class MyHandler extends Handler
    {
        WeakReference<Activity> mWeakReference;
        public MyHandler(Activity activity) 
        {
            mWeakReference=new WeakReference<Activity>(activity);
        }
        @Override
        public void handleMessage(Message msg)
        {
            final Activity activity=mWeakReference.get();
            if(activity!=null)
            {
                if (msg.what == 1)
                {
                    noteBookAdapter.notifyDataSetChanged();
                }
            }
        }
    }

  1. 非静态内部类导致的内存泄漏
    非静态的内部类会持有外部类的一个引用,所以和前面context说到的一样,如果该内部类生命周期超过外部类的生命周期,就可能引起内存泄露了,如AsyncTask和Handler。因为在Activity中我们可能会用到匿名内部类,所以要小心管理其生命周期。 如果明确生命周期较外部类长的话,那么应该使用静态内部类

  2. Context导致内存泄漏
    android 中很多地方都要用到context,连基本的Activty 和 Service都是从Context派生出来的,我们利用Context主要用来加载资源或者初始化组件,在Activity中有些地方需要用到Context的时候,我们经常会把context给传递过去了,将context传递出去就有可能延长了context的生命周期,最终导致了内存泄漏。例如 我们将activty context对象传递给一个后台线程去执行某些操作,如果在这个过程中因为屏幕旋转而导致activity重建,那么原先的activity对象不会被回收,因为它还被后台线程引用着,如果这个activity消耗了比较多的内存,那么新建activity或者后续操作可能因为旧的activity没有被回收而导致内存泄漏。所以,遇到需要用到context的时候,我们要合理选择不同的context,对于android应用来说还有一个单例的Application Context对象,该对象生命周期和应用的生命周期是绑定的。选择context应该考虑到它的生命周期,如果使用该context的组件的生命周期超过该context对象,那么我们就要考虑是否可以用application context。如果真的需要用到该context对象,可以考虑用弱引用来WeakReference来避免内存泄漏

### Android 中 OOMANR 的原因及解决方案 #### 一、OOM(Out of Memory) OOM 是指应用程序使用的内存超出了系统分配的最大限制。以下是其主要原因及其对应的解决方法: - **大图片加载** 加载高分辨率图片时容易占用大量内存,尤其是在低配设备上。可以通过压缩图片尺寸或使用高效的图像处理库来减少内存消耗[^1]。例如,`Glide` 或 `Picasso` 可以自动调整图片大小并缓存数据。 ```java // 使用 Glide 加载图片 Glide.with(context).load(imageUrl).into(imageView); ``` - **Bitmap 处理不当** 如果未及时回收 Bitmap 对象,则可能导致内存泄漏。应始终在不再需要时调用 `recycle()` 方法释放资源。 ```java if (bitmap != null && !bitmap.isRecycled()) { bitmap.recycle(); } ``` - **内存泄漏** 静态变量持有 Context 引用或者长时间存在的对象未能被垃圾回收器清理都会引发内存泄漏。可以借助工具如 LeakCanary 进行检测和修复[^2]。 --- #### 二、ANR(Application Not Responding) 当主线程阻塞超过 5 秒时会触发 ANR 提示框。常见原因是耗时操作放在了 UI 线程中执行。以下是一些典型场景以及应对策略: - **网络请求同步等待** 同步 HTTP 请求会在主线程挂起直到完成响应,这将直接导致界面冻结。改用异步方式可有效缓解该问题。 ```java new Thread(() -> { try { OkHttpClient client = new OkHttpClient(); Request request = new Request.Builder().url("http://example.com").build(); Response response = client.newCall(request).execute(); // 异步实现 } catch (IOException e) { e.printStackTrace(); } }).start(); ``` - **数据库查询时间过长** 数据读写应该放到后台线程里进行以免影响用户体验流畅度。Room Persistence Library 支持 LiveData 类型返回值从而简化多线程管理流程。 ```kotlin @Query("SELECT * FROM users WHERE id LIKE :id LIMIT 1") fun getUserById(id: String): LiveData<User> ``` - **广播接收器延迟过多** 广播监听器内部存在复杂计算逻辑也可能造成短暂停滞现象。建议把繁重任务交由 IntentService 来承担。 ```xml <receiver android:name=".MyBroadcastReceiver"> </receiver> ``` ```java public class MyIntentService extends IntentService { @Override protected void onHandleIntent(Intent intent) { // 执行耗时工作... } } ``` --- ### 结论 针对上述两种异常情况采取适当措施能够显著提升 APP 整体表现力,在实际项目开发当中还需要持续关注性能指标变化趋势以便快速定位潜在隐患。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值