内存优化

Android的内存机制

Android的程序由Java语言编写,所以Android的内存管理与Java的内存管理相似。程序员通过new为对象分配内存,所有对象在java堆内分配空间;然而对象的释放是由垃圾回收器来完成的。C/C++中的内存机制是“谁污染,谁治理”,java的就比较人性化了,给我们请了一个专门的清洁工(GC)。

那么GC怎么能够确认某一个对象是不是已经被废弃了呢?Java采用了有向图的原理。Java将引用关系考虑为图的有向边,有向边从引用者指向引用对象。 线程对象可以作为有向图的起始顶点,该图就是从起始顶点开始的一棵树,根顶点可以到达的对象都是有效对象,GC不会回收这些对象。如果某个对象 (连通子图)与这个根顶点不可达(注意,该图为有向图),那么我们认为这个(这些)对象不再被引用,可以被GC回收。

Android的内存溢出

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

Android的虚拟机是基于寄存器的Dalvik,它的最大堆大小一般是16M,有的机器为24M。因此我们所能利用的内存空间是有限的。如果我们的内存占用超过了一定的水平就会出现OutOfMemory的错误。

造成内存溢出的原因

为什么会出现内存不够用的情况呢?我想原因主要有两个:

长期保持某些资源(如Context)的引用,造成内存泄露,资源造成得不到释放。

保存了多个耗用内存过大的对象(如Bitmap),造成内存超出限制。

1、线程

线程是造成内存泄露的一个重要的源头。线程产生内存泄露的主要原因在于线程生命周期的不可控。

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

事实上,线程的问题并不仅仅在于内存泄露,还会带来一些灾难性的问题 。

2、Context

长期保持某些资源(如Context)的引用,如将Context的引用传给一个静态的对象

private static Drawable sBackground;  


@Override  

protected void onCreate(Bundle state) {  

  super.onCreate(state);  

  TextView label = new TextView(this);  

  label.setText("Leaks are bad");  

  if (sBackground == null) {  

    sBackground = getDrawable(R.drawable.large_bitmap);  

  }  

  label.setBackgroundDrawable(sBackground);  

  setContentView(label);  

}

sBackground是一个静态的变量,但是我们发现,我们并没有显式的保存Contex的引用,但是,当Drawable与View连接之后,Drawable就将View设置为一个回调,由于View中是包含Context的引用的,所以,实际上我们依然保存了Context的引用。这个引用链如下:
Drawable->TextView->Context,所以,最终该Context也没有得到释放,发生了内存泄露。
如何才能有效的避免这种引用的发生呢?

应该尽量避免static成员变量引用资源耗费过多的实例,比如Context。

Context尽量使用Application Context,因为Application的Context的生命周期比较长,引用它不会出现内存泄露的问题。

使用WeakReference代替强引用。比如可以使用WeakReference<Context> mContextRef;

3、Adapter

getView()方法中,如果我们不去使用 缓存的convertView,而是每次都在getView()中重新实例化一个View对象的话,即浪费资源也浪费时间,也会使得内存占用越来越大。

所以使用缓存和ViewHolder来减少内存占用。

4、Bitmap

Bitmap是内存大户,太多的Bitmap很容易就能将内存爆掉。

Bitmap对象在不使用时,我们应该先调用recycle()释放内存,然后才它设置为null。合理的使用压缩和缓存来减少Bitmap在内存中的数量和大小。

5、Cursor

Cursor是Android查询数据后得到的一个管理数据集合的类,正常情况下,如果查询得到的数据量较小时不会有内存问题,而且虚拟机能够保证Cusor最终会被释放掉。然而如果Cursor的数据量特表大,特别是如果里面有Blob信息时,应该保证Cursor占用的内存被及时的释放掉,而不是等待GC来处理。并且Android明显是倾向于编程者手动的将Cursor close掉,因为在源代码中我们发现,如果等到垃圾回收器来回收时,会给用户以错误提示。

有一种情况下,我们不能直接将Cursor关闭掉,这就是在CursorAdapter中应用的情况,但是注意,CursorAdapter在Acivity结束时并没有自动的将Cursor关闭掉,因此,你需要在onDestroy函数中,手动关闭。

6、io流

在使用文件或者访问网络资源时,使用了InputStream/OutputStream也会导致内存泄露,使用完要及时关闭。

7、集合

我们通常把一些对象的引用加入到了集合中,当我们不需要该对象时,并没有把它的引用从集合中清理掉,这样这个集合就会越来越大。如果这个集合是static的话,那情况就更严重了。code中务必及时清理。

posted @ 2015-11-02 18:06  Allin1579  阅读(119)  评论(0编辑  收藏  举报