Android内存优化总结【整理】
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。
•新开线程;
说明:
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>> //声明 private LruCache<String, // // if (Utils.hasHoneycomb()) //Android版本判断 mReusableBitmaps Collections.synchronizedSet( new HashSet<SoftReference<Bitmap>>()); //定义 } mMemoryCache new LruCache<String, // @Override protected void entryRemoved( boolean evicted, BitmapDrawable if (RecyclingBitmapDrawable. class .isInstance(oldValue)) // // ((RecyclingBitmapDrawable) false ); } else { // if (Utils.hasHoneycomb()) // // 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:利用系统资源
- 系统定义的id:如@android:id/list
- 系统的图片资源:如@*android:drawable/ic_menu_attachment
- 系统的文字串资源:如@android:string/yes
- 系统的Style:如android:textAppearance="?android:attr/textAppearanceMedium“
- 系统的颜色定义:如android:background ="@android:color/transparent"
说明:
Android系统本身有很多的资源,包括各种各样的字符串、图片、动画、样式和布局等等,这些都可以在应用程序中直接使用。这样做的好处很多,既可以减少内存的使用,又可以减少部分工作量,也可以缩减程序安装包的大小。
Android中没有公开的资源,在xml中直接引用会报错。除了去找到对应资源并拷贝到我们自己的应用目录下使用以外,我们还可以将引用“@android”改成“@*android”解决。
Tip 2:通用模块抽离
<include layout="@layout/navigator_bar" />
- 背景
- 头部标题栏
- 底部导航栏
- ListView
Tip 3:ViewStub
ViewStub是一个隐藏的,不占用内存空间的视图对象,它可以在运行时延迟加载布局资源文件。
定义:
使用:
三、Android 内存监测方法
3.1 adb
Pss列的TOTAL是APP使用的总内存,也就是在设置中正在运行里面看到的进程使用内存。
Pss列的Dalvik表示APP使用的堆内存。
Pss列的Other dev表示其它设备(如显卡)使用的内存,4.2系统上,开启硬件加速后,这个值会变得很大。
3.2 DDMS
3.3 Memory Analyzer(MAT)
Android内存优化总结【整理】的更多相关文章
- 大礼包!ANDROID内存优化(大汇总)
写在最前: 本文的思路主要借鉴了2014年AnDevCon开发者大会的一个演讲PPT,加上把网上搜集的各种内存零散知识点进行汇总.挑选.简化后整理而成. 所以我将本文定义为一个工具类的文章,如果你在A ...
- ANDROID内存优化——大汇总(转)
原文作者博客:转载请注明本文出自大苞米的博客(http://blog.csdn.net/a396901990),谢谢支持! ANDROID内存优化(大汇总——上) 写在最前: 本文的思路主要借鉴了20 ...
- ANDROID内存优化(大汇总——中)
转载请注明本文出自大苞米的博客(http://blog.csdn.net/a396901990),谢谢支持! 写在最前: 本文的思路主要借鉴了2014年AnDevCon开发者大会的一个演讲PPT,加上 ...
- 【腾讯Bugly干货分享】Android内存优化总结&实践
本文来自于腾讯Bugly公众号(weixinBugly),未经作者同意,请勿转载,原文地址:https://mp.weixin.qq.com/s/2MsEAR9pQfMr1Sfs7cPdWQ 导语 智 ...
- Android内存优化(二)DVM和ART的GC日志分析
相关文章 Android内存优化系列 Java虚拟机系列 前言 在Java虚拟机(三)垃圾标记算法与Java对象的生命周期这篇文章中,提到了Java虚拟机的GC日志.DVM和ART的GC日志与Java ...
- Android内存优化大全(中)
转载请注明本文出自大苞米的博客(http://blog.csdn.net/a396901990),谢谢支持! 写在最前: 本文的思路主要借鉴了2014年AnDevCon开发者大会的一个演讲PPT,加上 ...
- ANDROID内存优化(大汇总——全)
写在最前: 本文的思路主要借鉴了2014年AnDevCon开发者大会的一个演讲PPT,加上把网上搜集的各种内存零散知识点进行汇总.挑选.简化后整理而成. 所以我将本文定义为一个工具类的文章,如果你在A ...
- [转]探索 Android 内存优化方法
前言 这篇文章的内容是我回顾和再学习 Android 内存优化的过程中整理出来的,整理的目的是让我自己对 Android 内存优化相关知识的认识更全面一些,分享的目的是希望大家也能从这些知识中得到一些 ...
- Android内存优化之——static使用篇(使用MAT工具进行分析)
这篇文章主要配套与Android内存优化之——static使用篇向大家介绍MAT工具的使用,我们分析的内存泄漏程序是上一篇文章中static的使用内存泄漏的比较不容易发现泄漏的第二情况和第三种情况—— ...
- Android内存优化(三)详解内存分析工具MAT
前言 在这个系列的前四篇文章中,我分别介绍了DVM.ART.内存泄漏和内存检测工具的相关知识点,这一篇我们通过一个小例子,来学习如何使用内存分析工具MAT. 1.概述 在进行内存分析时,我们可以使用M ...
随机推荐
- 将string转换成char型的一般方法
C++文件读取中: infile in: in.open("file.dat",ios::in); 这样是能够的. 可是 string a; a="file.dat&qu ...
- The Qt Resource System
The Qt Resource System The Qt resource system is a platform-independent mechanism for storing binary ...
- iOS边练边学--UIPickerView和UIDatePicker的简单使用
一.点菜系统练习(UIPickerView) <1>UIPickerView的常用代理方法介绍 #pragma mark - <UIPickerViewDelegate> // ...
- Entity Framework应用:使用Code First模式管理视图
一.什么是视图 视图在RDBMS(关系型数据库管理系统)中扮演了一个重要的角色,它是将多个表的数据联结成一种看起来像是一张表的结构,但是没有提供持久化.因此,可以将视图看成是一个原生表数据顶层的一个抽 ...
- Winform控件:保存文件对话框(SaveFileDialog)
SaveFileDialog用于保存文件 1.新建Winform窗体应用程序,命名为SaveFileDialogDemo. 2.在界面上添加一个按钮的控件(用于打开保存文件对话框),添加文本控件,用于 ...
- js导出execl
var idTmr; function ExportExcel(tableid) {//整个表格拷贝到EXCEL中 var curTbl = document.getElementById(table ...
- 在hibernate中查询单个对象的方法,get()、load()、
查询单个对象可以直接通过Session对象来做到,其中session这个对象提过了2种获得单个对象的方法,一个是get方法和load方法,我去看这个两个方法的时候发现这两个方法的参数是一样的,使用方式 ...
- if、for、while、do 等语句自占一行
if.for.while.do 等语句自占一行,执行语句不得紧跟其后.不论 执行语句有多少都要加{}.这样可以防止书写失误. #include <iostream> /* run this ...
- pyqt的多Button的点击事件的槽函数的区分发送signal的按钮。
关键函数:QPushButton的setObjectName()/objectName() 个人注解:按功能或者区域,将按钮的点击事件绑定的不同的槽函数上. from PyQt5.QtWidgets ...
- 使用Ultra Librarian转换芯片的Altium Designer封装格式
第一步:找到对应芯片的CAD文件,以OPA350为例: http://www.ti.com/product/opa350 RE: 使用Ultra Librarian转换TI芯片的Altium De ...