• android的内存优化分析【转,超级推荐】


     一、 Android的内存机制

        Android的程序由Java语言编写,所以Android的内存管理与Java的内存管理相似。程序员通过new为对象分配内存,所有对象在java堆内分配空间;然而对象的释放是由垃圾回收器来完成的。C/C++中的内存机制是“谁污染,谁治理”,java的就比较人性化了,给我们请了一个专门的清洁工(GC)。

        那么GC怎么能够确认某一个对象是不是已经被废弃了呢?Java采用了有向图的原理。Java将引用关系考虑为图的有向边,有向边从引用者指向引用对象。线程对象可以作为有向图的起始顶点,该图就是从起始顶点开始的一棵树,根顶点可以到达的对象都是有效对象,GC不会回收这些对象。如果某个对象 (连通子图)与这个根顶点不可达(注意,该图为有向图),那么我们认为这个(这些)对象不再被引用,可以被GC回收。

    二、Android的内存溢出

        Android的内存溢出是如何发生的?

        Android的虚拟机是基于寄存器的Dalvik,它的最大堆大小一般是16M,有的机器为24M。因此我们所能利用的内存空间是有限的。如果我们的内存占用超过了一定的水平就会出现OutOfMemory的错误。

    为什么会出现内存不够用的情况呢?我想原因主要有两个:

    • 由于我们程序的失误,长期保持某些资源(如Context)的引用,造成内存泄露,资源造成得不到释放。
    • 保存了多个耗用内存过大的对象(如Bitmap),造成内存超出限制。

    三、万恶的static

        static是Java中的一个关键字,当用它来修饰成员变量时,那么该变量就属于该类,而不是该类的实例。所以用static修饰的变量,它的生命周期是很长的,如果用它来引用一些资源耗费过多的实例(Context的情况最多),这时就要谨慎对待了。

    1. public class ClassName {  
    2.      private static Context mContext;  
    3.      //省略  
    4. }  

    以上的代码是很危险的,如果将Activity赋值到么mContext的话。那么即使该Activity已经onDestroy,但是由于仍有对象保存它的引用,因此该Activity依然不会被释放。

        我们举Android文档中的一个例子。

    1. private static Drawable sBackground;  
    2.      
    3.  @Override  
    4.  protected void onCreate(Bundle state) {  
    5.    super.onCreate(state);  
    6.      
    7.    TextView label = new TextView(this);  
    8.    label.setText("Leaks are bad");  
    9.      
    10.    if (sBackground == null) {  
    11.      sBackground = getDrawable(R.drawable.large_bitmap);  
    12.    }  
    13.    label.setBackgroundDrawable(sBackground);  
    14.      
    15.    setContentView(label);  
    16.  }  

        sBackground, 是一个静态的变量,但是我们发现,我们并没有显式的保存Contex的引用,但是,当Drawable与View连接之后,Drawable就将View设置为一个回调,由于View中是包含Context的引用的,所以,实际上我们依然保存了Context的引用。这个引用链如下:

        Drawable->TextView->Context

        所以,最终该Context也没有得到释放,发生了内存泄露。

        如何才能有效的避免这种引用的发生呢?

        第一,应该尽量避免static成员变量引用资源耗费过多的实例,比如Context。

        第二、Context尽量使用Application Context,因为Application的Context的生命周期比较长,引用它不会出现内存泄露的问题。

        第三、使用WeakReference代替强引用。比如可以使用WeakReference<Context> mContextRef;

        该部分的详细内容也可以参考Android文档中Article部分。

    四、都是线程惹的祸

        线程也是造成内存泄露的一个重要的源头。线程产生内存泄露的主要原因在于线程生命周期的不可控。我们来考虑下面一段代码。

    1. public class MyActivity extends Activity {  
    2.     @Override  
    3.     public void onCreate(Bundle savedInstanceState) {  
    4.         super.onCreate(savedInstanceState);  
    5.         setContentView(R.layout.main);  
    6.         new MyThread().start();  
    7.     }  
    8.   
    9.     private class MyThread extends Thread{  
    10.         @Override  
    11.         public void run() {  
    12.             super.run();  
    13.             //do somthing  
    14.         }  
    15.     }  
    16. }  

        这段代码很平常也很简单,是我们经常使用的形式。我们思考一个问题:假设MyThread的run函数是一个很费时的操作,当我们开启该线程后,将设备的横屏变为了竖屏,一般情况下当屏幕转换时会重新创建Activity,按照我们的想法,老的Activity应该会被销毁才对,然而事实上并非如此。

        由于我们的线程是Activity的内部类,所以MyThread中保存了Activity的一个引用,当MyThread的run函数没有结束时,MyThread是不会被销毁的,因此它所引用的老的Activity也不会被销毁,因此就出现了内存泄露的问题。

     

        有些人喜欢用Android提供的AsyncTask,但事实上AsyncTask的问题更加严重,Thread只有在run函数不结束时才出现这种内存泄露问题,然而AsyncTask内部的实现机制是运用了ThreadPoolExcutor,该类产生的Thread对象的生命周期是不确定的,是应用程序无法控制的,因此如果AsyncTask作为Activity的内部类,就更容易出现内存泄露的问题。

        这种线程导致的内存泄露问题应该如何解决呢?

        第一、将线程的内部类,改为静态内部类。

        第二、在线程内部采用弱引用保存Context引用。

        解决的模型如下:

    1. public abstract class WeakAsyncTask<Params, Progress, Result, WeakTarget> extends  
    2.         AsyncTask<Params, Progress, Result> {  
    3.     protected WeakReference<WeakTarget> mTarget;  
    4.   
    5.     public WeakAsyncTask(WeakTarget target) {  
    6.         mTarget = new WeakReference<WeakTarget>(target);  
    7.     }  
    8.   
    9.     /** {@inheritDoc} */  
    10.     @Override  
    11.     protected final void onPreExecute() {  
    12.         final WeakTarget target = mTarget.get();  
    13.         if (target != null) {  
    14.             this.onPreExecute(target);  
    15.         }  
    16.     }  
    17.   
    18.     /** {@inheritDoc} */  
    19.     @Override  
    20.     protected final Result doInBackground(Params... params) {  
    21.         final WeakTarget target = mTarget.get();  
    22.         if (target != null) {  
    23.             return this.doInBackground(target, params);  
    24.         } else {  
    25.             return null;  
    26.         }  
    27.     }  
    28.   
    29.     /** {@inheritDoc} */  
    30.     @Override  
    31.     protected final void onPostExecute(Result result) {  
    32.         final WeakTarget target = mTarget.get();  
    33.         if (target != null) {  
    34.             this.onPostExecute(target, result);  
    35.         }  
    36.     }  
    37.   
    38.     protected void onPreExecute(WeakTarget target) {  
    39.         // No default action  
    40.     }  
    41.   
    42.     protected abstract Result doInBackground(WeakTarget target, Params... params);  
    43.   
    44.     protected void onPostExecute(WeakTarget target, Result result) {  
    45.         // No default action  
    46.     }  
    47. }  
  • 相关阅读:
    (Java) LeetCode 44. Wildcard Matching —— 通配符匹配
    (Java) LeetCode 30. Substring with Concatenation of All Words —— 与所有单词相关联的字串
    (Java) LeetCode 515. Find Largest Value in Each Tree Row —— 在每个树行中找最大值
    (Java) LeetCode 433. Minimum Genetic Mutation —— 最小基因变化
    (Java) LeetCode 413. Arithmetic Slices —— 等差数列划分
    (Java) LeetCode 289. Game of Life —— 生命游戏
    (Java) LeetCode 337. House Robber III —— 打家劫舍 III
    (Java) LeetCode 213. House Robber II —— 打家劫舍 II
    (Java) LeetCode 198. House Robber —— 打家劫舍
    (Java) LeetCode 152. Maximum Product Subarray —— 乘积最大子序列
  • 原文地址:https://www.cnblogs.com/xitang/p/2192453.html
Copyright © 2020-2023  润新知