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)
- 进程优先级:
- Foreground:处于焦点的进程。
- Visible:可以看见的进程。
- Service:服务进程。
- Background:后台进程。
- Empty:空进程。
- 进行GC时刻:
- app空闲的时候。
- 内存紧张的时候 。
- 分配大的内存块不够用的时候。
- GC回收模型:
Generational Heap Memory
采用分代技术,分为年轻代、老年代、持久代。
最先清理的是年轻代,只要清理出足够的空间就不去清理老年代了。依次持久带也是这样。 - GC回收步骤:
- 从GC Root开始,开始对对象引用遍历,找出GC Root直接引用的所有对象集1,再找出直接引用对象集1的对象集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.Options的inPreferredConfig方法设置。 - 图片回收:
// 先判断是否已经回收
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提供了ExecutorService和Executors类,我们可以用它们去创建线程池。
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"
- id
网友评论