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"
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,242评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,769评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,484评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,133评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,007评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,080评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,496评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,190评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,464评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,549评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,330评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,205评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,567评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,889评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,160评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,475评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,650评论 2 335

推荐阅读更多精彩内容