您的位置:首页 > 移动开发 > Android开发

android Cache——内存基本原理和机制管理

2016-05-13 11:03 537 查看
java语言相对于c/c++语言来说人性化的一点就是java有专门管理回收的垃圾回收器。而c/c++语言只能是“谁造成,谁处理”。

GC

1,GC是垃圾收集的意思(Gabage Collection)

2,Java提供的GC功能可以自动监测对象是否超过作用域从而达到自动回收内存的目的。

3,Java语言没有提供释放已分配内存的显示操作方法。

垃圾回收原理

垃圾回收器通常是作为一个单独的低级别的线程运行,不可预知的情况下对内存堆中已经死亡的或者长时间没有使用的对象进行清除和回收,程序员不能实时的调用垃圾回收器对某个对象或所有对象进行垃圾回收。

回收机制有分代复制垃圾回收标记垃圾回收增量垃圾回收

垃圾回收器原理

对于GC来说,当程序员创建对象时,GC就开始监控这个对象的地址、大小以及使用情况。

GC采用有向图的方式记录和管理堆(heap)中的所有对象。通过这种方式确定哪些对象是”可达的”,哪些对象是”不可达的”。当GC确定一些对象为”不可达”时,GC就有责任回收这些内存空间。

程序员可以手动执行System.gc(),通知GC运行,但是Java语言规范并不保证GC一定会执行。

Android中GC什么情况下会出现内存泄露?

Java 内存泄露的根本原因就是保存了不可能再被访问的变量类型的引用。

例如:一个静态方法引用了上下文(context),并把这个context设置成了全局变量。那么这个context就成了内存泄漏的“事发者”了。

/**
* 内存泄漏例子
*/
public class TestClass {

private static Context mContext;

//外界调用该方法,传入上下文,并传递对象。这个上下文就不会被内存回收
public static void setContext(Context context) {
mContext = context;
}

private void getContext() {
Toast.makeText(mContext, "内存泄漏了", Toast.LENGTH_LONG).show();
}
}


Android的内存溢出是如何发生的?

Android的虚拟机是基于寄存器的Dalvik,它的最大堆大小一般是16M,有的机器为24M

如果我们的内存占用超过了一定的水平就会出现OOM(OutOfMemory)的错误。

例如:

1,程序的失误,长期保持某些资源(如Context)的引用

2,保存了多个耗用内存过大的对象(如Bitmap)

一般情况就是:资源得不到释放占用内存过大的对象造成内存超出限制。

内存管理

1,static

当用static来修饰成员变量时,那么该变量就属于该类,而不是该类的实例。所以用static修饰的变量,它的生命周期是很长的,谨慎使用它来引用一些资源耗费过多的实例(Context的情况最多)。

还有一种比较容易忽视的现象:

private static Drawable sBackground;

public void example (){
ImageView iv = new ImageView(this);
iv.setBackgroundDrawable(sBackground);
}


如代码,我们将一个drawable设置成静态的,方便任何时候去调用这个对象。殊不知,这个静态对象已经造成了内存泄漏。

我们并没有显式的保存Contex的引用,但是,因为Imageview是View的子类,当Drawable与View连接之后,Drawable就将View设置为一个回调:(源码)

public class View implements Drawable.Callback

/**
* The application environment this view lives in.
* This field should be made private, so it is hidden from the SDK.
* {@hide}
*/
protected Context mContext;


由于View中是包含Context的引用的,所以,实际上我们依然保存了Context的引用。所以最终的引用链如下:

Drawable->ImageView->Context

所以,Context尽量使用Application Context,因为Application的Context的生命周期比较长;

使用WeakReference代替强引用。比如可以使用WeakReference mContextReference。

2,数据库游标

关于数据库的游标Cursor,在数据量小的时候我们是非常不容易察觉的。但是数据量大的时候,内存问题才会体现出来。

我们可以将Cursor里面的值给实现parcelable的类的成员变量,在响应的地方取值,这样可以及时关闭游标,释放资源。

3,线程

new Thread(new Runnable() {
public void run() {
// TODO do something
}
}).start();


当开启一个线程后,在线程中执行耗时操作。这个时候,如果这个线程还在执行。执行了横竖屏切换,并且new了一个activity。

这时候,老activity中的线程还在执行,所以新activity执行的时候,老activity不会被销毁。

这样,因为这个线程是内部类,所以引用了这个activity,只要run方法不结束,这个老activity就不会被销毁,造成了内存泄漏。

另外,异步任务AsyncTask的问题更加严重。

Thread只有在run函数不结束时才出现这种内存泄露问题,然而AsyncTask内部的实现机制是运用了ThreadPoolExcutor,该类产生的Thread对象的生命周期是不确定的,是应用程序无法控制的,因此如果AsyncTask作为Activity的内部类,就更容易出现内存泄露的问题。

那么,如果遇到这种情况,怎么解决呢?

1,将线程的内部类,改为静态内部类。

2,在线程内部采用弱引用保存Context引用。

4,Bitmap

它是一个“超级大胖子”,特别是分辨率大的图片,如果要显示多张那问题就更显著了。

解决办法:

1,及时销毁。在用完Bitmap时,要及时的recycle掉。recycle并不能确定立即就会将Bitmap释放掉,但是会给虚拟机一个暗示:“该图片可以释放了”。所以Bitmap对象在不使用时,我们应该先调用recycle()释放内存,然后才它设置为null. 虽然recycle()从源码上看,调用它应该能立即释放Bitmap的主要内存,但是测试结果显示它并没能立即释放内存。

2,设置采样率。记载一个缩小过的图片,只要能够供用户观看就可以了。用完Bitmap时,要及时的recycle掉。

3,巧妙的运用软引用(SoftRefrence)。我们使用Bitmap后没有保留对它的引用,因此就无法调用Recycle函数。这时候巧妙的运用软引用,可以使Bitmap在内存快不足时得到有效的释放。

5,Adapter

就listview或者gridview的适配器来说,不做优化的后果估计都知道。

关键在于图片:

private ArrayList<SoftReference<Bitmap>> mBitmaps = new ArrayList<SoftReference<Bitmap>>();


和view的复用:ViewHolder。

6,将变量的作用域设置为最小。

如果一个变量是方法级的,那么当这个方法执行完毕后,这个变量就会被回收掉。

而如果这个变量是类级的,那么垃圾回收机制必须等到该类的所有引用都被移除后才能被回收。

所以,变量的作用域要根据实际情况而定。

7,仅仅当一个对象确实需要的时候才初始化

一些对象可能在我们定义它的时候就初始化了。那么,如果在调用这个对象之前出现任何问题,这个对象所占的内存就被浪费了。

所以,真正当我们需要用到一个对象的前一步再去初始化。

8,不要再循环中定义变量

这是一个常见的错误。

public void test() {
for (int i = 0; i < 20; i++) {
String name = "";
name += i;
Log.i("test", name);
}
}


这样你可以循环出所有的名字。但是每一次程序都会创建String name;然后回收;这样做会额外的分配内存也会增加垃圾回收的器的负担。

另外,在循环中String如果使用+来链接会是一个很大的开销。这时候String会是一个很长的查询或者产生一个toString,如果我们用+来链接,可想而知,是多么大的内存开销。

这时候尽量使用不变对象,如:StringBuffer来进行字符串的链接。

如果要重引用对象,这时候将对象设置成null。这样,内存能够立即释放它的内存。

9,使用finally块

try/catch方法不管success还是final都会调用finally方法。我们可以将清理内存的语句放在finally中以保证内存被清理。

10,分散对象创建和删除的时间

集中创建大量对象,特别是大对象的时候需要大量的内存。GC在这个时候会回收内存和正合内存碎片。这时候其实是增加了主GC的频率,使得下一次创建新对象的时候强制主GC的机会大大增加。

好了,这就是android内存的基本知识。只要在编程中注意到了这些问题,那么你的程序会大大的得到优化甚至便于维护。不要认为项目的优化是从框架到逻辑的一个过程,其实很多时候是从这些小细节出发的。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: