阅读背景:

Android解决内存溢出问题

来源:互联网 

2011-03-11 00:03:57标签:内存泄漏 android OutOfMemory Bitmap Cursor版权声明:原创作品,拒绝转述!否则将追究法律义务。 一、 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),造成内存超越限制。三、万恶的static static是Java中的一个症结字,当用它来润饰成员变量时,那末该变量就属于该类,而不是该类的实例。所以用static润饰的变量,它的性命周期是很长的,如果用它来援用一些资源消耗过量的实例(Context的情形最多),这时候就要谨严看待了。public class ClassName { private static Context mContext; //省略 } 以上的代码是很危险的,如果将Activity赋值到么mContext的话。那末即便该Activity已onDestroy,但是由于仍有对象保留它的援用,因此该Activity仍然不会被释放。 我们举Android文档中的一个例子。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 mContextRef; 该部份的详细内容也能够参考Android文档中Article部份。四、都是线程惹的祸 线程也是造成内存泄漏的一个主要的源头。线程发生内存泄漏的主要缘由在于线程性命周期的不可控。我们来斟酌下面一段代码。public class MyActivity extends Activity { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); new MyThread().start(); } private class MyThread extends Thread{ @Override public void run() { super.run(); //do somthing } } } 这段代码很平凡也很简略,是我们常常应用的情势。我们思考一个问题:假定MyThread的run函数是一个很费时的操作,当我们开启该线程后,将装备的横屏变成了竖屏,一般情形下当屏幕转换时会重新创立Activity,依照我们的想法,老的Activity应当会被烧毁才对,但是事实上并不是如此。 由于我们的线程是Activity的内部类,所以MyThread中保留了Activity的一个援用,当MyThread的run函数没有停止时,MyThread是不会被烧毁的,因此它所援用的老的Activity也不会被烧毁,因此就涌现了内存泄漏的问题。 有些人爱好用Android供给的AsyncTask,但事实上AsyncTask的问题更加严重,Thread只有在run函数不停止时才涌现这类内存泄漏问题,但是AsyncTask内部的实现机制是应用了ThreadPoolExcutor,该类发生的Thread对象的性命周期是不肯定的,是应用程序没法掌握的,因此如果AsyncTask作为Activity的内部类,就更容易涌现内存泄漏的问题。 这类线程致使的内存泄漏问题应当如何解决呢? 第一、将线程的内部类,改成静态内部类。 第二、在线程内部采取弱援用保留Context援用。 解决的模型以下:public abstract class WeakAsyncTask extends AsyncTask { protected WeakReference mTarget; public WeakAsyncTask(WeakTarget target) { mTarget = new WeakReference(target); } /** {@inheritDoc} */ @Override protected final void onPreExecute() { final WeakTarget target = mTarget.get(); if (target != null) { this.onPreExecute(target); } } /** {@inheritDoc} */ @Override protected final Result doInBackground(Params... params) { final WeakTarget target = mTarget.get(); if (target != null) { return this.doInBackground(target, params); } else { return null; } } /** {@inheritDoc} */ @Override protected final void onPostExecute(Result result) { final WeakTarget target = mTarget.get(); if (target != null) { this.onPostExecute(target, result); } } protected void onPreExecute(WeakTarget target) { // No default action } protected abstract Result doInBackground(WeakTarget target, Params... params); protected void onPostExecute(WeakTarget target, Result result) { // No default action } } 事实上,线程的问题其实不单单在于内存泄漏,还会带来一些灾害性的问题。由于本篇文章讨论的是内存问题,所以在此不做讨论。2011-03-11 00:03:57标签:内存泄漏 android OutOfMemory Bit




你的当前访问异常,请进行认证后继续阅读剩余内容。

分享到: