http://blog.csdn.net/tiantangrenjian/article/details/39182293

【前段时间接到任务着手进行app的内存优化,从各种各样的渠道搜索相关资料,最后汇总整理如下。】


一、Android 内存管理

1.1 Dalvik

Dalvik虚拟机是Android程序的虚拟机,是android中Java程序的运行基础。其指令集基于寄存器架构,执行其特有的文件格式——dex字节码来完成对象生命周期管理、堆栈管理、线程管理、安全异常管理、垃圾回收等重要功能。

Dalvik虚拟机的内存大体上可以分为 JavaObject Heap、Bitmap Memory和Native Heap三种。

javaObject Heap:用于分配对象

Bitmap Memory:用来处理图像,≥Android 3.0, 归到Object Heap中

Native Heap: malloc分配,受系统限制

1.2 查看最大内存限制

ActivityManager mActivityManager = (ActivityManager)getSystemService(Context.ACTIVITY_SERVICE);
int memoryClass = am. getMemoryClass();              //96MB [Mi2S,Android 4.1]

可以通过在AndroidManifest.xml文件中设置来扩大内存限制:(不过不建议使用此参数,建议节省内存)

int largeMemoryClass = am. getLargeMemoryClass();            //384MB  [Mi2S,Android 4.1]

1.3 垃圾回收(GC)

作用:自动回收不再被引用的Java Object

 
       

1.4 垃圾回收机制

在2.3之前:

1. Stop-the-world :也就是垃圾收集线程在执行的时候,其它的线程都停止;

2. Full heap collection:一次收集完全部的垃圾;

3. Pause time:≥100ms:一次垃圾收集造成的程序中止时间通常都大于100ms。

在2.3以及更高的版本中:

1. Cocurrent:大多数情况下,垃圾收集线程与其它线程是并发执行的;

2. Partial collection:一次可能只收集一部分垃圾;

3. Pause time:≤5ms:一次垃圾收集造成的程序中止时间通常都小于5ms。

1.5 OutOfMemory Error

主要原因:

1.内存泄露:程序中存在对无用对象的引用,导致GC无法回收。

2.内存超限:保存了多个耗用内存过大的对象(如Bitmap)。

二、内存优化

  • Coding
  • Bitmap
  • ListView
  • SoftReference & WeakReference
  • UI

2.1 Coding

 Tip 1:使用优化过的数据容器。

前提:Key为Integer类型

HashMap 是内存低效的,因为每一个mapping都需要单独的entry(如下图)。

SparseArray可以避免AutoBox,查找方法为二分查找,效率比HashMap低一些,但百量级以内性能差距不大。

HashMap<Integer, E> hashMap = new HashMap<Integer, E>();

SparseArray<E> sparseArray = new SparseArray<E>();、

Tip 2:使用IntentService替代Service。

•新开线程;

•顺序处理Intent;
•执行完自动退出。

说明:

IntentService继承自Service,所以它也是一个服务。

IntentService与Service区别:
1,Service默认在UI线程执行;而IntentService的onHandleIntent方法在后台执行。
2,Service在start后,如果没有手动stop会一直存在;而IntentService在执行完后自动退出。

Tip 3:尽量避免使用Enum。

枚举相对于静态常量来说,需要两倍甚至更多的内存。如下是Enum的类型定义:

Tip 4:使用混淆器移除不必要的代码。

ProGuard工具通过移除无用代码,使用语意模糊来保留类,字段和方法来压缩,优化和混淆代码。可以使你的代码更加完整,更少的RAM 映射页。

Tip 5:尽量不要因一两个特性而使用大体积类库。

Tip 6:频繁修改时使用 StringBuffer(Thread-Safe)或 StringBuilder(Thread-Unsafe)。

使用String修改字符串时,若修改后字符串在字符串常量区不存在,便会新生成一个String对象。

Tip 7: 对于常量,请尽量使用static final。

如果使用final定义常量之后,会减少编译器在类生成时初始化<clinit>方法调用时对常量的存储,对于int型常量,将会直接使用其数值来进行替换,而对于String对象将会使用相对廉价的“string
constant”指令来替换字段查找表。虽然这个方法并不完全对所有类型都有效,但是,将常量声明为static final绝对是一个好的做法。

Tip 8:对象不用时最好显式置为Null。

对象不用时最好显式置为Null可以减少GC开销。

警惕:静态变量引起内存泄露

这段代码中有一个静态的Resources对象。代码片段mResources = this.getResources()对Resources对象进行了初始化。这时Resources对象拥有了当前Activity对象的引用,Activity又引用了整个页面中所有的对象。

如果当前的Activity被重新创建(比如横竖屏切换,默认情况下整个Activity会被重新创建),由于Resources引用了第一次创建的Activity,就会导致第一次创建的Activity不能被垃圾回收器回收,从而导致第一次创建的Activity中的所有对象都不能被回收。这个时候,一部分内存就浪费掉了。

警惕:使用Activity Context引起内存泄露。

应该尽量使用Application Context。

在Android中,Application Context的生命周期和应用的生命周期一样长,而不是取决于某个Activity的生命周期。如果想保持一个长期生命的对象,并且这个对象需要一个Context,就可以使用Application对象。

看使用的周期是否在activity周期内,如果超出,必须用application;常见的情景包括:AsyncTask,Thread,第三方库初始化等等。

还有些情景,只能用activity:比如,对话框,各种View,需要startActivity的等。总之,尽可能使用Application。

上面由于静态变量导致的内存泄露问题,可以修改如下:

2.2 Bitmap

Tip:捕获异常

因为Bitmap是吃内存大户,为了避免应用在分配Bitmap内存的时候出现OutOfMemory异常以后Crash掉,需要特别注意实例化Bitmap部分的代码。通常,在实例化Bitmap的代码中,一定要对OutOfMemory异常进行捕获。

OutOfMemoryError是一种Error,而不是Exception。

Tip:缓存通用的图像【该方法测试无效】

应用场景:默认头像。有时候,可能需要在一个Activity里多次用到同一张图片。比如一个Activity会展示一些用户的头像列表,而如果用户没有设置头像的话,则会显示一个默认头像,而这个头像是位于应用程序本身的资源文件中的。

此方法无效!因为:

因为Android自带资源文件缓存机制:

在Resource.java类中有LongSparseArray<WeakReference<Drawable.ConstantState> > mDrawableCache

每次会new 一个Drawable,但内部bitmap还是指向cache中的。

Tip:压缩图片

BitmapFactory.Options options = new BitmapFactory.Options();

options.inSampleSize = 2;

如果图片像素过大,使用BitmapFactory类的方法实例化Bitmap的过程中,就会发生OutOfMemory异常。

使用BitmapFactory.Options设置inSampleSize就可以缩小图片。属性值inSampleSize表示缩略图大小为原始图片大小的几分之一。即如果这个值为2,则取出的缩略图的宽和高都是原始图片的1/2,图片的大小就为原始大小的1/4。

如果知道图片的像素过大,就可以对其进行缩小。那么如何才知道图片过大呢?

使用BitmapFactory.Options设置inJustDecodeBounds为true后,再使用decodeFile()等方法,并不会真正的分配空间,即解码出来的Bitmap为null,但是可计算出原始图片的宽度和高度,即options.outWidth和options.outHeight。通过这两个值,就可以知道图片是否过大了。

先获取图片真实的宽度和高度,然后判断是否需要跑缩小。如果不需要缩小,设置inSampleSize的值为1。如果需要缩小,则动态计算并设置inSampleSize的值,对图片进行缩小。

Tip:及时回收Bitmap的内存 (≤Android 2.3.3,API10)

Bitmap类的构造方法都是私有的,所以开发者不能直接new出一个Bitmap对象,只能通过BitmapFactory类的各种静态方法来实例化一个Bitmap。

仔细查看BitmapFactory的源代码可以看到,生成Bitmap对象最终都是通过JNI调用方式实现的。所以,加载Bitmap到内存里以后,是包含两部分内存区域的。简单的说,一部分是Java部分的,一部分是C部分的。这个Bitmap对象是由Java部分分配的,不用的时候系统就会自动回收了,但是那个对应的C可用的内存区域,虚拟机是不能直接回收的,这个只能调用底层的功能释放。所以需要调用recycle()方法来释放C部分的内存。从Bitmap类的源代码也可以看到,recycle()方法里也的确是调用了JNI方法了的。

Tip: BitmapFactory.Options.inBitmap (≥Android 3.0,API11)

作用:内存重用

定义和存储:

Set<SoftReference<Bitmap>>
mReusableBitmaps;        
//声明
private LruCache<String,
BitmapDrawable> mMemoryCache;
 
//
If you're running on Honeycomb or newer, create a
//
synchronized HashSet of references to reusable bitmaps.
if (Utils.hasHoneycomb())
{         
//Android版本判断
    mReusableBitmaps
=
            Collections.synchronizedSet(new HashSet<SoftReference<Bitmap>>());      //定义
}
 
mMemoryCache
= 
new LruCache<String,
BitmapDrawable>(mCacheParams.memCacheSize) {
 
    //
Notify the removed entry that is no longer being cached.
    @Override
    protected void entryRemoved(boolean evicted,
String key,
            BitmapDrawable
oldValue, BitmapDrawable newValue) {
        if (RecyclingBitmapDrawable.class.isInstance(oldValue))
{
            //
The removed entry is a recycling drawable, so notify it
            //
that it has been removed from the memory cache.
            ((RecyclingBitmapDrawable)
oldValue).setIsCached(
false);
        } else {
            //
The removed entry is a standard BitmapDrawable.
            if (Utils.hasHoneycomb())
{
                //
We're running on Honeycomb or later, so add the bitmap
                //
to a SoftReference set for possible use with inBitmap later.
                mReusableBitmaps.add
                        (new SoftReference<Bitmap>(oldValue.getBitmap()));    //存入从LRUCache中删除的对象
            }
        }
    }
....
}

使用方法详见:https://developer.android.com/training/displaying-bitmaps/manage-memory.html#inBitmap

限制:

before Android 4.4 (API level 19):宽度及高度必须完全相等

after:尺寸小于等于既有内存

2.3 ListView

Tip:从ContentView获取缓存的view

如果不使用缓存convertView的话,调用getView时每次都会重新创建View,这样之前的View可能还没有销毁,加之不断的新建View势必会造成内存泄露。

Tip:使用ViewHolder模式来避免没有必要的调用findViewById()

ViewHolder模式通过getView()方法返回的视图的标签(Tag)中存储一个数据结构,这个数据结构包含了指向我们要绑定数据的视图的引用,从而避免每次调用getView()的时候调用findViewById()。

2.4 SoftReference & WeakReference

软引用

只有当内存空间不足了,才会回收这些对象的内存。

软引用可用来实现内存敏感的高速缓存。

Map<String, SoftReference<Bitmap>> imageCache = new HashMap<String, SoftReference<Bitmap>>();

弱引用

被垃圾回收器扫描到后即被回收。

Map<String,WeakReference<Bitmap>> cacheMap = new HashMap<String, WeakReference<Bitmap>>();

只具有弱引用的对象拥有更短暂的生命周期。在垃圾回收器线程扫描它所管辖的内存区域的过程中,一旦发现了只具有 弱引用的对象,不管当前内存空间足够与否,都会回收它的内存。不过,由于垃圾回收器是一个优先级很低的线程,因此不一定会很快发现那些只具有弱引用的对 象。

 案例:异步加载网络图片

详见:http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2013/0920/1554.html

2.5 UI

Tip 1:利用系统资源

说明:

Android系统本身有很多的资源,包括各种各样的字符串、图片、动画、样式和布局等等,这些都可以在应用程序中直接使用。这样做的好处很多,既可以减少内存的使用,又可以减少部分工作量,也可以缩减程序安装包的大小。

Android中没有公开的资源,在xml中直接引用会报错。除了去找到对应资源并拷贝到我们自己的应用目录下使用以外,我们还可以将引用“@android”改成“@*android”解决。

Tip 2:通用模块抽离

<include layout="@layout/navigator_bar" />

  • 背景
  • 头部标题栏
  • 底部导航栏
  • ListView

Tip 3:ViewStub

ViewStub是一个隐藏的,不占用内存空间的视图对象,它可以在运行时延迟加载布局资源文件。

定义:

使用:

三、Android 内存监测方法

3.1 adb

adb root
adb shell procrank | grep "com...."【包名】查看概要内存使用情况
或者
adb shell dumpsys meminfo com.*【包名】查看较详细内存使用情况
我们比较关注的几项:
Pss列的TOTAL是APP使用的总内存,也就是在设置中正在运行里面看到的进程使用内存。
Pss列的Dalvik表示APP使用的堆内存。
Pss列的Other dev表示其它设备(如显卡)使用的内存,4.2系统上,开启硬件加速后,这个值会变得很大。

3.2 DDMS

3.3 Memory Analyzer(MAT)

 

Android内存优化总结【整理】的更多相关文章

  1. 大礼包!ANDROID内存优化(大汇总)

    写在最前: 本文的思路主要借鉴了2014年AnDevCon开发者大会的一个演讲PPT,加上把网上搜集的各种内存零散知识点进行汇总.挑选.简化后整理而成. 所以我将本文定义为一个工具类的文章,如果你在A ...

  2. ANDROID内存优化——大汇总(转)

    原文作者博客:转载请注明本文出自大苞米的博客(http://blog.csdn.net/a396901990),谢谢支持! ANDROID内存优化(大汇总——上) 写在最前: 本文的思路主要借鉴了20 ...

  3. ANDROID内存优化(大汇总——中)

    转载请注明本文出自大苞米的博客(http://blog.csdn.net/a396901990),谢谢支持! 写在最前: 本文的思路主要借鉴了2014年AnDevCon开发者大会的一个演讲PPT,加上 ...

  4. 【腾讯Bugly干货分享】Android内存优化总结&实践

    本文来自于腾讯Bugly公众号(weixinBugly),未经作者同意,请勿转载,原文地址:https://mp.weixin.qq.com/s/2MsEAR9pQfMr1Sfs7cPdWQ 导语 智 ...

  5. Android内存优化(二)DVM和ART的GC日志分析

    相关文章 Android内存优化系列 Java虚拟机系列 前言 在Java虚拟机(三)垃圾标记算法与Java对象的生命周期这篇文章中,提到了Java虚拟机的GC日志.DVM和ART的GC日志与Java ...

  6. Android内存优化大全(中)

    转载请注明本文出自大苞米的博客(http://blog.csdn.net/a396901990),谢谢支持! 写在最前: 本文的思路主要借鉴了2014年AnDevCon开发者大会的一个演讲PPT,加上 ...

  7. ANDROID内存优化(大汇总——全)

    写在最前: 本文的思路主要借鉴了2014年AnDevCon开发者大会的一个演讲PPT,加上把网上搜集的各种内存零散知识点进行汇总.挑选.简化后整理而成. 所以我将本文定义为一个工具类的文章,如果你在A ...

  8. [转]探索 Android 内存优化方法

    前言 这篇文章的内容是我回顾和再学习 Android 内存优化的过程中整理出来的,整理的目的是让我自己对 Android 内存优化相关知识的认识更全面一些,分享的目的是希望大家也能从这些知识中得到一些 ...

  9. Android内存优化之——static使用篇(使用MAT工具进行分析)

    这篇文章主要配套与Android内存优化之——static使用篇向大家介绍MAT工具的使用,我们分析的内存泄漏程序是上一篇文章中static的使用内存泄漏的比较不容易发现泄漏的第二情况和第三种情况—— ...

  10. Android内存优化(三)详解内存分析工具MAT

    前言 在这个系列的前四篇文章中,我分别介绍了DVM.ART.内存泄漏和内存检测工具的相关知识点,这一篇我们通过一个小例子,来学习如何使用内存分析工具MAT. 1.概述 在进行内存分析时,我们可以使用M ...

随机推荐

  1. FusionCharts JavaScript API - Functions 常用方法整理笔记

    FusionCharts JavaScript API - Functions Home > FusionCharts XT and JavaScript > API Reference  ...

  2. for语句联系 -小九九乘法表

    public class a { /** * @param args */ public static void main(String[] args) { // TODO 自动生成的方法存根 int ...

  3. scsi线

  4. REST构架风格介绍:状态表述转移(转)

    本文总结了一些REST风格构架设计的经验,介绍了REST架构的语言生态环境,状态表述转移,无状态服务器等特点,并通过举例Google来说明REST风格的味道. REST(Representationa ...

  5. 常见JVM监控工具用法介绍

    VisualVM是一种集成了多个JDK命令行工具的可视化工具,它能为您提供强大的分析能力.所有这些都是免费的!它囊括的命令行工具包括jps,jstat,jmap,jinfo,jstack,JConso ...

  6. 【转】java基本数据类型vs封装数据类型

    1.基本概念 说java是面向对象的语言是正确的,但是她不纯,基本数据类型就不是对象. 基本数据类型可以大致分为三类:数据型:int.short.long.byte.float.double字符型:c ...

  7. Qt/C++ 构造函数与explicit

    1.默认构造函数 默认构造函数是指所有参数都提供了默认值的构造函数,通常指无参的构造函数或提供默认值的构造函数.如类Test1和Test2的构造函数 class Test1 { public: Tes ...

  8. e621. Activating a Keystroke When Any Child Component Has Focus

    Normally, a keystroke registered on a component is activated when the component has the focus. This ...

  9. 电脑的CPU可直接解读的数据机器码

    机器语言(machine language)是一种指令集的体系.这种指令集,称机器码(machine code),是电脑的CPU可直接解读的数据. 机器码有时也被称为原生码(Native Code), ...

  10. The configuration file 'appsettings.json' was not found and is not optional

    问: .Net Core: Application startup exception: System.IO.FileNotFoundException: The configuration file ...