`
Z_萧晓
  • 浏览: 11289 次
  • 性别: Icon_minigender_1
  • 来自: 上海
社区版块
存档分类
最新评论

最常见的Android内存优化方式及防止泄漏造成OOM总结篇

阅读更多


前言

内存优化目的就是让我们在开发中怎么有效的避免我们的应用出现内存泄漏的问题。内存泄漏大家都不陌生了,简单粗俗的讲,就是该被释放的对象没有释放,一直被某个或某些实例所持有却不再被使用导致 GC 不能回收。既然说到内存泄漏和优化,就不得不先简单了解一下内存分配策略,然后再举常见泄漏例子和解决方法,最后做一下总结,这样更直观全面了解Android内存方面处理。

 

内存分配

内存分配策略有三种,分别是静态、栈式和堆式。对应的的内存空间主要分别是静态存储区(也称方法区)、栈区和堆区。如下:

  • 静态存储区:主要存放静态数据、全局 static 数据和常量。这块内存在程序编译时就已经分配好,并且在程序整个运行期间都存在。
  • 栈区 :当方法被执行时,方法体内的局部变量都在栈上创建,并在方法执行结束时这些局部变量所持有的内存将会自动被释放。因为栈内存分配运算内置于处理器的指令集中,效率很高,但是分配的内存容量有限。
  • 堆区 : 又称动态内存分配,通常就是指在程序运行时直接 new 出来的内存。这部分内存在不使用时将会由 Java 垃圾回收器来负责回收。

栈与堆 区别:

在方法体内定义的(局部变量)一些基本类型的变量和对象的引用变量都是在方法的栈内存中分配的。当在一段方法块中定义一个变量时,Java 就会在栈中为该变量分配内存空间,当超过该变量的作用域后,该变量也就无效了,分配给它的内存空间也将被释放掉,该内存空间可以被重新使用。堆内存用来存放所有由 new 创建的对象(包括该对象其中的所有成员变量)和数组。在堆中分配的内存,将由 Java 垃圾回收器来自动管理。在堆中产生了一个数组或者对象后,还可以在栈中定义一个特殊的变量,这个变量的取值等于数组或者对象在堆内存中的首地址,这个特殊的变量就是我们上面说的引用变量。我们可以通过这个引用变量来访问堆中的对象或者数组。

举个例子:

public class Sample() {
 
    int s1 = 0;
    Sample mSample1 = new Sample();
    public void method() {
        int s2 = 1;
        Sample mSample2 = new Sample();
    }
}
Sample mSample3 = new Sample();

Sample 类的局部变量 s2 和引用变量 mSample2 都是存在于栈中,但 mSample2 指向的对象是存在于堆上的。

 

mSample3 指向的对象实体存放在堆上,包括这个对象的所有成员变量 s1 和 mSample1,而它自己存在于栈中。

 

小结

 

局部变量的基本数据类型和引用存储于栈中,引用的对象实体存储于堆中:因为它们属于方法中的变量,生命周期随方法而结束。

 

成员变量全部存储与堆中(包括基本数据类型,引用和引用的对象实体):因为它们属于类,类对象终究是要被new出来使用的

 

管理内存

内存管理就是对象的分配和释放问题。

 

在 Java 中,程序员需要通过关键字 new 为每个对象申请内存空间 (基本类型除外),所有的对象都在堆 (Heap)中分配空间。另外,对象的释放是由 GC 决定和执行的。在 Java 中,内存的分配是由程序完成的,而内存的释放是由 GC 完成的,这种收支两条线的方法确实简化了程序员的工作。但同时,它也加重了JVM的工作。这也是 Java 程序运行速度较慢的原因之一。因为,GC 为了能够正确释放对象,GC 必须监控每一个对象的运行状态,包括对象的申请、引用、被引用、赋值等,GC 都需要进行监控。监视对象状态是为了更加准确地、及时地释放对象,而释放对象的根本原则就是该对象不再被引用。

 

内存泄漏

内存泄漏就是存在一些被分配的对象,这些对象有下面两个特点,首先,这些对象是可达的,即在有向图中,存在通路可以与其相连;其次,这些对象是无用的,即程序以后不会再使用这些对象。如果对象满足这两个条件,这些对象就可以判定为Java中的内存泄漏,这些对象不会被GC所回收,然而它却占用内存。

 

在C++中,内存泄漏的范围更大一些。有些对象被分配了内存空间,然后却不可达,由于C++中没有GC,这些内存将永远收不回来。在Java中,这些不可达的对象都由GC负责回收,因此程序员不需要考虑这部分的内存泄露。

 

通过分析,我们得知,对于C++,程序员需要自己管理边和顶点,而对于Java程序员只需要管理边就可以了(不需要管理顶点的释放)。通过这种方式,Java提高了编程的效率。

 

因此,通过以上分析,我们知道在Java中也有内存泄漏,但范围比C++要小一些。因为Java从语言上保证,任何对象都是可达的,所有的不可达对象都由GC管理。

 

对于程序员来说,GC基本是透明的,不可见的。虽然,我们只有几个函数可以访问GC,例如运行GC的函数System.gc(),但是根据Java语言规范定义, 该函数不保证JVM的垃圾收集器一定会执行。因为,不同的JVM实现者可能使用不同的算法管理GC。通常,GC的线程的优先级别较低。JVM调用GC的策略也有很多种,有的是内存使用到达一定程度时,GC才开始工作,也有定时执行的,有的是平缓执行GC,有的是中断式执行GC。但通常来说,我们不需要关心这些。除非在一些特定的场合,GC的执行影响应用程序的性能,例如对于基于Web的实时系统,如网络游戏等,用户不希望GC突然中断应用程序执行而进行垃圾回收,那么我们需要调整GC的参数,让GC能够通过平缓的方式释放内存,例如将垃圾回收分解为一系列的小步骤执行,Sun提供的HotSpot JVM就支持这一特性。

 

Java 内存泄漏的典型例子:

Vector v = new Vector(10);
for (int i = 1; i < 100; i++) {
    Object o = new Object();
    v.add(o);
    o = null;   
}

在这个例子中,我们循环申请Object对象,并将所申请的对象放入一个 Vector 中,如果我们仅仅释放引用本身,那么 Vector 仍然引用该对象,所以这个对象对 GC 来说是不可回收的。因此,如果对象加入到Vector 后,还必须从 Vector 中删除,最简单的方法就是将 Vector 对象设置为 null。

 

常见内存泄漏

(本篇重点)

1、集合类泄漏

如果一个集合类是全局性变量(比如类中的静态变量或全局性map即有静态引用又或者final指向它)只有添加元素的方法,而没有相应的清除机制,就会占用内存只增不减,造成内存泄漏。比如我们通常用HashMap做一些缓存之类的事,这种情况就多留点心,做好相应删除机制。

2、单例造成泄漏

由于单例的静态性使得生命周期跟应用的生命周期一样长,很容易造成内存泄漏。

典型的例子

public class AppManager {
    private static AppManager instance;
    private Context context; 
 
    private AppManager(Context context) {
           this.context = context;
    }
 
    public static AppManager getInstance(Context context) {
      if (instance != null) {
          instance = new AppManager(context);
      }
      return instance;
    }
}

这是一个普通的单例模式,当创建这个单例的时候,由于需要传入一个Context,所以这个Context的生命周期的长短至关重要:

 

1:如果此时传入的是 Application 的 Context,因为 Application 的生命周期就是整个应用的生命周期,所以这将没有任何问题。

2:如果此时传入的是 Activity 的 Context,当这个 Context 所对应的 Activity 退出时,由于该 Context 的引用被单例对象所持有,其生命周期等于整个应用程序的生命周期,所以当前 Activity 退出时它的内存并不会被回收,这就造成泄漏了

3、非静态内部类创建静态实例造成的内存泄漏

有的时候我们可能会在启动频繁的Activity中,为了避免重复创建相同的数据资源,可能会出现这种写法:

public class MainActivity extends AppCompatActivity { 
       private static TestResource mResource = null; 
       @Override 
       protected void onCreate(Bundle savedInstanceState) { 
      super.onCreate(savedInstanceState); 
      setContentView(R.layout.activity_main); 
      if(mManager == null){ 
         mManager = new TestResource(); 
      } 
       } 
       class TestResource { 
       //… 
       } 
}

这样就在Activity内部创建了一个非静态内部类的单例TestResource,每次启动Activity时都会使用该单例的数据,这样虽然避免了资源的重复创建,不过这种写法却会造成内存泄漏,因为非静态内部类默认会持有外部类的引用,而该非静态内部类又创建了一个静态的实例,该实例的生命周期和应用的一样长,这就导致了该静态实例一直会持有该Activity的引用,导致Activity的内存资源不能正常回收。

 

正确的做法为:将该内部类设为静态内部类或将该内部类抽取出来封装成一个单例,如果需要使用Context,请按照上面推荐的使用Application 的Context。当然,Application 的 context 不是万能的,所以也不能随便乱用,对于有些地方则必须使用 Activity 的 Context,对于Application,Service,Activity三者的Context的应用场景如下:Application 和 Service 可以启动一个 Activity,不过需要创建一个新的 task 任务队列。而对于 Dialog 而言,只有在 Activity 中才能创建。

4、匿名内部类线程异步导致泄漏

在继承实现Activity/Fragment/View时,此时如果你使用了匿名类,并被异步线程持有了,那要小心了,没有任何措施一定会导致泄露。 举个栗子:

public class MainActivity extends Activity { 
  ...{
    Runnable re1 = new MyRunable(); 
    Runnable re2 = new Runnable() { 
    @Override 
    public void run() { 
    } 
     }; 
}

re1和re2的区别是,re2使用了匿名内部类。运行时这两个引用的内存可以看到,re1没什么特别的。

但ref2这个匿名类的实现对象里面多了一个引用:this$0这个引用指向MainActivity.this。

也就是说当前的MainActivity实例会被re2持有,如果将这个引用再传入一个异步线程,此线程和此Acitivity生命周期不一致的时候,就造成了Activity的泄露。

5、Handler 造成的内存泄漏

handler为了避免ANR而不在主线程进行耗时操作,去处理网络任务或者封装一些请求回调等api等。我们知道 Handler、Message 和 MessageQueue 都是相互关联在一起的,万一 Handler 发送的 Message 尚未被处理,则该 Message 及发送它的 Handler 对象将被线程 MessageQueue 一直持有造成内存泄漏。

 

由于 Handler 属于 TLS(Thread Local Storage) 变量, 生命周期和 Activity 是不一致的。因此这种实现方式一般很难保证跟 View 或者 Activity 的生命周期保持一致,故很容易导致无法正确释放。

 

知识点:在java里 ,非静态内部类 和匿名类都会潜在的引用它们所属的外部类。但是静态内部类却不会。

 

接下里看个案例:

public class SampleActivity extends Activity {
 
  private final Handler mHandler = new Handler() {
    @Override
    public void handleMessage(Message msg) {
      // ...
    }
  }
 
  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
 
    // Post a message and delay its execution for 10 minutes.
    mHandler.postDelayed(new Runnable() {
      @Override
      public void run() { ... }}, 1000 * 60 * 10);
 
    // Go back to the previous Activity.
    finish();
  }
}

分析:

当activity结束(finish)时,里面的延时消息在得到处理前,会一直保存在主线程的消息队列里持续10分钟。而且,由上文可知这个message持有handler引用,而handler又持有对其外部类(activity)的潜引用。这条引用关系会一直保持直到消息得到处理,从而阻止了acitivty被垃圾回收器回收,造成应用程序的泄漏。另外非静态匿名类Runnable同样持有外部类,导致泄漏。总结2条原因:

 

小结:

  • 只要有未处理的消息,那么消息会引用handler,非静态的handler又会引用外部类,即Activity,导致Activity无法被回收,造成泄漏;
  • Runnable类属于非静态匿名类,同样会引用外部类。

 

解决方案:

  • 我们可以把handler类放在单独的类文件中,或者使用静态内部类便可以避免泄漏。另外,如果想要在handler内部去调用所在的外部类Activity,那么可以在handler内部使用弱引用的方式指向所在Activity,这样统一不会导致内存泄漏。
  • 对于匿名类Runnable,同样可以将其设置为静态类。因为静态的匿名类不会持有对外部类的引用。

 

再看源码:

public class SampleActivity extends Activity {
 
  private static class MyHandler extends Handler {
    private final WeakReference<SampleActivity> mActivity;
 
    public MyHandler(SampleActivity activity) {
      mActivity = new WeakReference<SampleActivity>(activity);   //弱引用
    }
 
    @Override
    public void handleMessage(Message msg) {
      SampleActivity activity = mActivity.get();
      if (activity != null) {
        // ...
      }
    }
  }
 
  private final MyHandler mHandler = new MyHandler(this);
 
 
  private static final Runnable sRunnable = new Runnable() { //静态匿名类
      @Override
      public void run() { /* ... */ }
  };
 
  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
 
    // Post a message and delay its execution for 10 minutes.
    mHandler.postDelayed(sRunnable, 1000 * 60 * 10);
 
    // Go back to the previous Activity.
    finish();
  }
}

如果一个内部类实例的生命周期比Activity更长,那么我们千万不要使用非静态的内部类。最好的做法是,使用静态内部类,然后在该类里使用弱引用来指向所在的Activity。综述,推荐使用静态内部类 + WeakReference 这种方式。每次使用前注意判空。

 

知识点:

 

前面提到了 WeakReference,所以这里就简单的说一下 Java 对象的几种引用类型。

Java对引用的分类有强(Strong reference),软(SoftReference),弱 (WeakReference),虚 PhatomReference 四种。

 

在Android应用的开发中,为了防止内存溢出,在处理一些占用内存大而且声明周期较长的对象时候,可以尽量应用软引用和弱引用技术。

 

软/弱引用可以和一个引用队列(ReferenceQueue)联合使用,如果软引用所引用的对象被垃圾回收器回收,Java虚拟机就会把这个软引用加入到与之关联的引用队列中。利用这个队列可以得知被回收的软/弱引用的对象列表,从而为缓冲器清除已失效的软/弱引用。

 

假设我们的应用会用到大量的默认图片,比如应用中有默认的头像,默认游戏图标等等,这些图片很多地方会用到。如果每次都去读取图片,由于读取文件需要硬件操作,速度较慢,会导致性能较低。所以我们考虑将图片缓存起来,需要的时候直接从内存中读取。但是,由于图片占用内存空间比较大,缓存很多图片需要很多的内存,就可能比较容易发生OutOfMemory异常。这时,我们可以考虑使用软/弱引用技术来避免这个问题发生。以下就是高速缓冲器的雏形:首先定义一个HashMap,保存软引用对象——private Map。

6、尽量避免使用 static 成员变量

如果成员变量被声明为 static,那我们都知道其生命周期将与整个app进程生命周期一样。

这会导致一系列问题,如果你的app进程设计上是长驻内存的,那即使app切到后台,这部分内存也不会被释放。按照现在手机app内存管理机制,占内存较大的后台进程将优先回收,如果此app做过进程互保保活,那会造成app在后台频繁重启。当手机安装了你参与开发的app以后一夜时间手机被消耗空了电量、流量,你的app不得不被用户卸载或者静默。

 

修复的方法:

  • 不要在类初始时初始化静态成员。可以考虑lazy初始化。

7、AsyncTask对象造成的泄漏

AsyncTask确实需要额外注意一下。它的泄露原理和前面Handler,Thread泄露的原理差不多,它的生命周期和Activity不一定一致。

 

解决方案:在activity退出的时候,终止AsyncTask中的后台任务。

 

但是,问题是如何终止?

 

AsyncTask提供了对应的API:public final boolean cancel (boolean mayInterruptIfRunning)。

它的说明有这么一句话:

// Attempts to cancel execution of this task. This attempt will fail if the task has already completed, already been cancelled, or could not be cancelled for some other reason.
// If successful, and this task has not started when cancel is called, this task should never run. If the task has already started, then the mayInterruptIfRunning parameter determines whether the thread executing this task should be interrupted in an attempt to stop the task.

cancel是不一定成功的,如果正在运行,它可能会中断后台任务。怎么感觉这话说的这么不靠谱呢?

是的,就是不靠谱。

那么,怎么才能靠谱点呢?我们看看官方的示例:

private class DownloadFilesTask extends AsyncTask<URL, Integer, Long> {
     protected Long doInBackground(URL... urls) {
         int count = urls.length;
         long totalSize = 0;
         for (int i = 0; i < count; i++) {
             totalSize += Downloader.downloadFile(urls[i]);
             publishProgress((int) ((i / (float) count) * 100));
             // Escape early if cancel() is called
             // 注意下面这行,如果检测到cancel,则及时退出
             if (isCancelled()) break;
         }
         return totalSize;
     }
 
     protected void onProgressUpdate(Integer... progress) {
         setProgressPercent(progress[0]);
     }
 
     protected void onPostExecute(Long result) {
         showDialog("Downloaded " + result + " bytes");
     }
 }

官方的例子是很好的,在后台循环中时刻监听cancel状态,防止没有及时退出。

为了提醒大家,google特意在AsyncTask的说明中撂下了一大段英文:

// AsyncTask is designed to be a helper class around Thread and Handler and does not constitute a generic threading framework. AsyncTasks should ideally be used for short operations (a few seconds at the most.) If you need to keep threads running for long periods of time, it is highly recommended you use the various APIs provided by the java.util.concurrent pacakge such as Executor, ThreadPoolExecutor and FutureTask.

可怜我神州大陆幅员辽阔,地大物博,什么都不缺,就是缺对英语阅读的敏感。

AsyncTask适用于短耗时操作,最多几秒钟。如果你想长时间耗时操作,请使用其他java.util.concurrent包下的API,比如Executor, ThreadPoolExecutor 和 FutureTask.

学好英语,避免踩坑!

8、 BroadcastReceiver对象

种种原因没有调用到unregister()方法。

解决方法很简单,就是确保调用到unregister()方法。

顺带说一下,我在工作中碰到一种相反的情况,receiver对象没有registerReceiver()成功(没有调用到),于是unregister的时候提示出错:

// java.lang.IllegalArgumentException: Receiver not registered ...

解决方案:

方案一:在registerReceiver()后设置一个FLAG,根据FLAG判断是否unregister()。网上搜到的文章几乎都这么写,我以前碰到这种bug,也是一直都这么解。但是不可否认,这种代码看上去确实有点丑陋。

方案二:我后来无意中听到某大牛提醒,在Android源码中看到一种更通用的写法:

// just sample, 可以写入工具类
// 第一眼我看到这段代码,靠,太粗暴了,但是回头一想,要的就是这么简单粗暴,不要把一些简单的东西搞的那么复杂。
private void unregisterReceiverSafe(BroadcastReceiver receiver) {
    try {
        getContext().unregisterReceiver(receiver);
    } catch (IllegalArgumentException e) {
        // ignore
    }
}

9、BitMap对象造成的泄漏

Bitmap 对象不用的时候最好调用一下recycle 方法再赋值null,清空资源的直接或间接引用,但是有人要问,android源码里面好多地方也没有调用啊?

是的,我这里说的是最好,如果不调用的话,只能依赖于Java GC 执行的时候,调用Bitmap 的 finalize方法,

这里面会执行navtive的方法 nativeDestructor() 去释放资源,其实查看一下那个函数,就是一句 delete bitmap。

 

总结

1.对 Activity 等组件的引用应该控制在 Activity 的生命周期之内; 如果不能就考虑使用 getApplicationContext 或者 getApplication,以避免 Activity 被外部长生命周期的对象引用而泄露。

2.尽量不要在静态变量或者静态内部类中使用非静态外部成员变量(包括context ),即使要使用,也要考虑适时把外部成员变量置空;也可以在内部类中使用弱引用来引用外部类的变量。

3.对于生命周期比Activity长的内部类对象,并且内部类中使用了外部类的成员变量,可以将内部类改为静态内部类、静态内部类中使用弱引用来引用外部类的成员变量 。

4.Handler 的持有的引用对象最好使用弱引用,资源释放时也可以清空 Handler 里面的消息。比如在 Activity onStop 或者 onDestroy 的时候,取消掉该 Handler 对象的 Message和 Runnable。

5.在 Java 的实现过程中,也要考虑其对象释放,最好的方法是在不使用某对象时,显式地将此对象赋值为 null,比如使用完Bitmap 后先调用 recycle(),再赋为null,清空对图片等资源有直接引用或者间接引用的数组(使用 array.clear() ; array = null)等,最好遵循谁创建谁释放的原则。

6.正确关闭资源,对于使用了BraodcastReceiver,ContentObserver,File,游标 Cursor,Stream,Bitmap等资源的使用,应该在Activity销毁时及时关闭或者注销。

7.保持对对象生命周期的敏感,特别注意单例、静态对象、全局性集合等的生命周期。

 

不管怎么说,大家技术还是要学好的。小编下面给大家分享一份成为高级工程师学习路线,如果想学习高级UI、性能优化、移动架构师、 NDK、混开发等Android高阶开发的朋友可以加下我的Android架构群:887084983,还有免费的学习资料及面试资料领取~

 

好了,文章到这里就结束了,如果你觉得文章写得不错就给个赞呗?如果你觉得那里值得改进的,请给我留言。一定会认真查询,修正不足。谢谢。

 

如果你觉得还算有用的话,不妨把它们推荐给你的朋友。

  • 大小: 85.1 KB
分享到:
评论

相关推荐

    Android内存OOM优化详解.pdf

    Android内存优化是提升应用性能和用户体验的关键因素,尤其是在防止Out Of Memory (OOM)错误方面。本文将深入探讨Android内存管理的基础、内存优化策略、Bitmap的使用及管理、内存泄漏的原因和解决方案,以及如何...

    Android 图片下载以及内存处理防止OOM内存溢出 源码

    - `图片下载及内存优化`可能是包含实际代码实现的文件,包括图片下载类、内存缓存机制以及防止OOM的逻辑。 通过对这些知识点的实践和深入理解,开发者可以构建出更加健壮、性能优秀的Android应用,同时提高用户...

    深入探索Android内存优化1

    【深入探索Android内存优化1】 Android内存优化是开发者必须掌握的关键技能之一,它涉及到应用程序的稳定性和用户体验。本文将深入探讨内存优化的相关概念、工具、管理机制以及常见问题,帮助开发者构建一个完整的...

    android 图片下载 防止OOM

    本篇文章将详细探讨如何在Android中进行图片下载并防止OOM。 ### 1. OOM概述 OOM是Java虚拟机在分配内存时遇到的问题,当应用程序请求的内存超过系统可分配的最大值,系统无法满足需求,导致程序崩溃。在Android中...

    android listview内存优化

    9. **避免内存泄漏**:注意管理ListView中的动态添加或删除的View,以及监听器的注册和注销,防止泄露导致内存占用持续升高。 10. **适时释放资源**:在ListView不再使用或Activity销毁时,及时释放ListView和...

    android解决OOM

    为了解决这个问题,开发者需要深入理解Android内存管理机制,并采取一系列优化策略。以下是一些关于如何解决Android OOM的关键知识点: 1. **Android内存模型**: - Dalvik/ART虚拟机:Android早期采用Dalvik,...

    android图片瀑布流优化版,防止内存溢出

    在Android开发中,图片瀑布流布局是一种常见的展示方式,它以多列的形式排列图片,每列高度不一,形成类似瀑布的效果。然而,如果不进行适当的优化,这种布局很容易导致内存溢出(Out Of Memory,OOM),尤其是在...

    android 永远不会oom的瀑布流

    综上所述,构建一个“永远不会OOM”的瀑布流需要综合运用多种优化策略,包括图片优化、View复用、分页加载、内存监听、异步加载以及内存泄漏检测等。结合现代的Android开发库和最佳实践,我们可以有效地管理内存,...

    android图片墙lrucache oom

    在Android开发中,图片墙是一种常见的应用场景,通常用于展示大量图片,如社交应用的动态流、电商应用...同时,开发者还应该时刻关注应用的内存使用情况,及时释放不再使用的资源,防止内存泄漏,确保应用的健康运行。

    Android相册图片解决OOM问题

    1. **内存泄漏**:内存泄漏是导致OOM的常见原因之一。当一个对象不再使用时,如果没有正确地释放,就会持续占用内存。在Android中,非静态内部类、单例模式、静态变量引用非静态对象等都可能导致内存泄漏。 2. **大...

    Android防止内存溢出浅析.zip

    在Android应用开发中,内存溢出(Out Of Memory,简称OOM)是一个常见的问题,它会导致应用崩溃,严重影响用户体验。理解并防止Android应用中的内存溢出是优化应用性能的关键环节。以下是对Android防止内存溢出的...

    Android例子源码仿oom的三例瀑布流源码

    2. **内存优化策略**: - **Bitmap的优化**:Bitmap对象占用了大量内存,应合理使用` BitmapFactory.Options `进行解码,限制大小并使用内存缓存。 - **使用软引用和弱引用**:通过使用SoftReference和...

    android之Gif处理(解决GIF显示容易OOM问题,包括代码和说明),非常详细的介绍

    在Android开发中,GIF...处理Android中的GIF并防止OOM需要综合运用多种策略,包括选择合适的GIF库、合理缩放、优化渲染以及注意内存监控。只有这样,才能确保应用在提供流畅的GIF体验的同时,保持良好的性能和稳定性。

    Android 内存溢出问题

    本篇文章将深入探讨Android内存管理的两个核心概念:堆(Heap)和栈(Stack),以及如何理解和解决Android内存溢出问题。 1. 堆与栈 堆和栈是Java虚拟机(JVM)中的两种主要内存区域,它们各自具有特定的用途和...

    处理android bitmap oom

    10. **监控内存使用**:使用Android Studio的内存分析工具,定期检查和调试内存泄漏,确保Bitmap被正确释放。 以上策略结合使用,可以有效地处理Android中的Bitmap OOM问题。在开发过程中,要持续关注性能和内存...

    处理android bitmap oom 2.0版本

    综上所述,处理Android Bitmap OOM的关键在于合理控制Bitmap的内存占用,通过解码、缓存、回收和配置优化等方式进行有效管理。在2.0版本中,开发者修复了之前版本中关于Bitmap回收的错误,以提供更加健壮和安全的...

    Android内存优化杂谈

    Android内存优化是我们性能优化工作中比较重要的一环,这里其实主要包括两方面的工作: 1、优化RAM,即降低运行时内存。这里的目的是防止程序发生OOM异常,以及降低程序由于内存过大被LMK机制杀死的概率。另一方面,...

    Android-OOM.rar_memory android_memory for Android_out

    在Android开发过程中,"Out Of Memory"(OOM)错误是一个常见的问题,特别是在处理大量数据、图像或者长时间运行的任务时...总的来说,理解Android内存管理机制并采取有效的内存优化策略,是防止和解决OOM问题的关键。

    Android应用程序如何避免内存泄漏

    因此,在onCreate()等方法中分配的资源应在onDestroy()中释放,以防止泄漏。对于大图加载,可以考虑使用Bitmap的内存缓存策略,或者使用LruCache等缓存机制。 3. **避免匿名内部类和非静态内部类持有外部类引用**:...

    安卓gif加载解决oom

    首先,理解Android内存管理机制是解决问题的关键。Android系统为每个应用分配一定的内存预算,当应用超过这个预算时,就会抛出OOM异常。对于大内存消耗的资源,如高分辨率的图片或动态图,不合理的加载和缓存策略...

Global site tag (gtag.js) - Google Analytics