Android 内存管理与优化

1.Android 内存基础

  • 所有的内存都是基于物理内存的,即移动设备上的RAM。当启动一个Android程序时,会启动一个Dalvik vm 进程,系统会给它分配固定的内存空间,这块内存会映射到RAM上某个区域,然后Android程序就运行在这块空间上。JAVA里会将这块空间分成Stack栈内存Heap堆内存。stack里存放对象的引用,heap里存放实际对象数据。

2.Android 问题与调节

  • Memory Leak(内存泄露):在程序中创建对象,不及时回收无效空间。
  • OutOfMemory(内存溢出):当应用程序申请的java heap空间超过Dalvik vm HeapGrowthLimit时,溢出。(内存溢出并不是内存不够,而是超过了Dalvik vm的限制)
  • Memory Churn(内存抖动):GC操作频繁,导致内存抖动。
  • 调节:如果RAM申请的空间不足,那么Android的Memory Killer会杀死优先级低的进程。

3.Android默认内存回收机制——垃圾回收GC(Garbage Collection)

  • 进程优先级:
    1. Foreground:处于焦点的进程。
    2. Visible:可以看见的进程。
    3. Service:服务进程。
    4. Background:后台进程。
    5. Empty:空进程。
  • 进行GC时刻:
    1. app空闲的时候。
    2. 内存紧张的时候 。
    3. 分配大的内存块不够用的时候。
  • GC回收模型:
    Generational Heap Memory
    采用分代技术,分为年轻代、老年代、持久代。


    最先清理的是年轻代,只要清理出足够的空间就不去清理老年代了。依次持久带也是这样。
  • GC回收步骤:
    1. 从GC Root开始,开始对对象引用遍历,找出GC Root直接引用的所有对象集1,再找出直接引用对象集1的对象集2……直到遍历完所有对象。这些对象都是有效对象
    2. 删除其他无效对象。
  • 注:GC执行的时候,当前所有线程的任何操作都会需要暂停。

4.优化——减少额外的内存使用

4.1 Bitmap

基本思路:缩略图,像素,图片回收,捕获OOM异常

  • 缩略图:使用BitmapFactory.Options方法里的inSampleSize设置缩小比。
  • 像素:
    Android中图片有四种属性,分别是:
    ALPHA_8:每个像素占用1byte内存
    ARGB_4444:每个像素占用2byte内存
    ARGB_8888:每个像素占用4byte内存 (默认)
    RGB_565:每个像素占用2byte内存
    默认是显示效果最好的ARGB_8888,可以使用BitmapFactory.OptionsinPreferredConfig方法设置。
  • 图片回收:
// 先判断是否已经回收  
if(bitmap != null && !bitmap.isRecycled()){  
    // 回收并且置为null  
    bitmap.recycle();  
    bitmap = null;  
}  
System.gc();  
  • 捕获OOM异常:
Bitmap bitmap = null;  
try {  
    // 实例化Bitmap  
    bitmap = BitmapFactory.decodeFile(path);  
} catch (OutOfMemoryError e) {  
    // 捕获OutOfMemoryError,避免直接崩溃  
}  
if (bitmap == null) {  
    // 如果实例化失败 返回默认的Bitmap对象  
    return defaultBitmapMap;  
}  

4.2 软引用和弱引用

软件用和弱引用的概念解释

  • 想避免OutOfMemory异常的发生,则可以使用软引用。
  • 想要提高应用性能,尽快回收占用内存更大的对象,可以使用弱引用。

4.3 小Tips

  • Context的引用,具体请看这篇Android Context详解
  • 避免创建不必要的对象:你要频繁操作一个字符串时,使用StringBuffer代替String。
  • 避免使用浮点数:在Android设备中,浮点数会比整型慢两倍。
  • 循环:尽量避免在for的条件参数(即())中访问成员变量,调用方法,例如:
for(int i=0;i<a.length;i++)//访问成员变量,会慢很多。
for(int i=0;i<a.length();i++)//调用方法,也会慢很多。

正确的做法如下:

int b = a.length/int b=a.length();
for(int i=0;i<b;i++)//这样循环体最快。

java还有一种访问数组的方法,这种方法比普通循环多出4个字节,因为产生了一个额外的变量,如下列的a。

for(Foo a:Array){
    m+=a.value;
}
  • 了解并使用类库
    当你在处理字串的时候,不要吝惜使用String.indexOf()String.lastIndexOf()等特殊实现的方法。这些方法都是使用C/C++实现的,比起Java循环快10到100倍。
    android.text.format包下的Formatter类,提供了IP地址转换、文件大小转换等方法;DateFormat类,提供了各种时间转换,都是非常高效的方法。
    TextUtils类,对于字符串处理Android为我们提供了一个简单实用的TextUtils类,如果处理比较简单的内容不用去思考正则表达式不妨试试这个在android.text.TextUtils的类。

5.优化—— 重复使用内存资源

  • 将已经存在的内存资源重新使用而避免去创建新的。最典型的使用就是**缓存(Cache)和池(Pool)。
  • 缓存:
    一种是内存缓存,一种是硬盘缓存。
    • 内存缓存(LruCache):存在宝贵的内存中。
    • 硬盘缓存(DiskLruCache):存在硬盘中。
  • 池:
    一种是对象池,一种是线程池。
    • 对象池:将用过的对象保存起来,等下一次使用时,再拿出来使用, 从而在一定情况下减少创造对象所产生的开销。但是不是任何对象都适合使用对象池,因为维护对象池也需要一定的开支。一定要“维护对象池的开支”小于“创建对象的开支”
    • 线程池:基本思想任然是对象池的思想,在开辟的一片空间里,存着很多线程,线程池的调度由池管理器来处理。当需要一个线程时,从池中取一个,用完后重新归池,减少了创建线程的开支。
      java提供了ExecutorServiceExecutors类,我们可以用它们去创建线程池。

6.优化——回收不需要的内存

  • 回收主要是Dalvik的GC机制。下面来讲一些具体的:

  • 线程回收:
    线程中涉及的任何东西GC都不能回收,所以线程很容易造成内存泄露。

Thread t = new Thread() {  
    public void run() {  
        while (true) {  
            try {  
                Thread.sleep(1000);  
                System.out.println("thread is running...");  
            } catch (InterruptedException e) {             
            }  
        }  
    }  
};  
t.start();  
t = null;  
System.gc();  

这个时候GC并不会回收这个线程,因为正在运行的线程属于GCRoot的一种,并不会被GC进行回收。

  • 游标回收:
    Cursor是Android查询数据后得到的一个管理数据集合的类,应该在使用完之后立即手动进行关闭。一般使用如下:
Cursor cursor = null;  
try {  
    cursor = mContext.getContentResolver().query(uri,null, null,null,null);  
    if(cursor != null) {  
        cursor.moveToFirst();  
    }  
} catch (Exception e) {  
    e.printStackTrace();  
} finally {  
    if (cursor != null) {  
        cursor.close();  
    }  
}  

:在CursorAdapter中应用时,我们不能直接关掉Cursor,而且CursorAdapter在Acivity结束时并没有自动的将Cursor关闭掉,因此,需要在onDestroy函数中,手动关闭。

@Override    
protected void onDestroy() {          
    if (mAdapter != null && mAdapter.getCurosr() != null) {    
        mAdapter.getCursor().close();    
    }    
    super.onDestroy();     
}    
  • Receiver(接收器)回收
    当在Activity进行了Receiver注册时,需要注意最后要注销。
    即调用registerReceiver(),使用完毕后调用unregisterReceiver()。
    一般在onDestroy()进行回收。
@Override    
protected void onDestroy() {    
      this.unregisterReceiver(receiver);    
      super.onDestroy();    
}    
  • Stream/File(流/文件)回收:
    各种流/文件如:
    InputStream/OutputStream,SQListeOpenHelper,SQLiteDatabase,Cursor,File,I/O,Bitmap都需要关闭。

7.优化——视图布局优化

  • 使用HierarchyViewer查看并减少OverDraw。
  • ViewStub标签:可以使布局在可见与不可见之间切换,默认不可见的情况下不占用内存。
  • include标签:复用UI资源。
  • merge标签:解决include标签的问题,减少一个层级,并且方便使用。
  • 重用系统资源:使用系统自带的资源(sdk\platforms\android-x\data\res),例如:
    • id
      android:id="@android:id/list"
    • 字符串
      @android:string/yes
    • Style
      android:textAppearance="?android:attr/textAppearanceMedium"
    • 颜色
      android:background ="@android:color/transparent"


作者:KaelQ
链接:https://www.jianshu.com/p/8db5cb0084f6
來源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值