内存溢出html
实质应用程序不能及时释放内存或者加载到内存上的数据太大而致使的OOM问题java
内存泄漏android
目标类被其余类持有、致使没法销毁、从而致使目标类没法被调用也没法销毁。缓存
窗体(内存)泄露数据结构
是指Activity或者Fragment在Destory的状况下、自身引用被其余对象或者线程持有,没法销毁。app
栈内存溢出框架
StackOverflowError:应用程序调用中,致使栈空间无限延长,超过了虚拟机的承载能力异步
app使用内存超过3/4而且不是当前的app,那么系统会回收内存(android.app.ActivityThread)ide
http://www.2cto.com/kf/201512/453248.html
(空进程,死亡进程,后台进程)
省电方面
消息队列遵循先进先出(First in First out)的原则来说某些信息或者任务进行排队等待。android中有本身的消息队列,如Handler和Broadcastreceiver。
andriod提供了 Handler 和 Looper 来知足线程间的通讯。Handler 先进先出原则。Looper类用来管理特定线程内对象之间的消息交换(Message Exchange)。 1)Looper: 一个线程能够产生一个Looper对象,由它来管理此线程里的Message Queue(消息队列)。 2)Handler: 你能够构造Handler对象来与Looper沟通,以便push新消息到Message Queue里;或者接收Looper从Message Queue取出)所送来的消息。 3) Message Queue(消息队列):用来存放线程放入的消息。 4)线程:UI thread 一般就是main thread,而Android启动程序时会替它创建一个Message Queue。
Handler的集中建立方式和用法
Handler mHandler = new Handler() { public void handleMessage(Message msg) { switch (msg.what) { case TestHandler.GUIUPDATEIDENTIFIER: myBounceView.invalidate(); break; } super.handleMessage(msg); } };
在工做线程中建立(异步)Handler
class LooperThread extends Thread { public Handler mHandler; public void run() { Looper.prepare(); mHandler = new Handler() { public void handleMessage(Message msg) { } }; Looper.loop(); }
使用HandlerThread建立(异步)Handler
HandlerThread handlerThread = new HandlerThread("my.handlerthread"); handlerThread.start(); Handler mHandler = new Handler(handlerThread.getLooper()){ public void handleMessage(Message msg) { } };
Handler的用法不少,这里只贴出线程通讯的用法,读者能够自行深刻研究其余用法
class myThread implements Runnable { public void run() { while (!Thread.currentThread().isInterrupted()) { //这里可使用 new,由于主线程中的消息队列只有一条 Message message = new Message(); message.what = 1024; TmHandler.sendMessage(message); try { Thread.sleep(100); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } } } }
1.1>Handler关于内存泄露
当咱们这样写在一个Activity中时,Android Lint会提示咱们这样一个 warning: In Android, Handler classes should be static or leaks might occur.。 意思说:在Android中,Handler 类应该是静态的不然可能发生泄漏 一样在Eclipse代码编辑区域的 Handler定义行也会出现相似的警告,通常都会加上supresslint的注解
先来看看,为何会出现这种问题:
1.当Android程序第一次建立的时候,在主线程同时会建立一个Looper对象。Looper实现了一个简单的消息队列,一个接着一个处理Message对象。 程序框架全部主要的事件(例如:屏幕上的点击时间,Activity生命周期的方法等等)都包含在Message对象中,而后添加到Looper的消息队列中, 一个一个处理。主线程的Looper存在整个应用程序的生命周期内。
2.当一个Handler对象在主线程中建立的时候,它会关联到Looper的 message queue 。Message添加到消息队列中的时候Message 会持有当前Handler引用,当Looper处理到当前消息的时候,会调用Handler#handleMessage(Message).
3.在java中,no-static的内部类会 隐式的 持有当前类的一个引用。static的类则没有。
综上三点可知,这种泄露很是危险,Activity或者被持有者(宿主)不能及时释放内存,Looper也在不断循环,是致使内存泄露缘由之一,
缘由之二是 Activity未被回收,当Activity处于非活动状态时,若是handlerMessage的处理致使UI的改变,将会致使窗体泄露,好比弹框,UI改变等。
1.2定义静态类
/** * 使用静态的内部类,不会持有当前对象的引用 */ private static class MyHandler extends Handler { private SoftReference<Activity> mActivityReference = null; public MyHandler(SampleActivity activity) { mActivityReference = new SoftReference<Activity>(activity) } @Override public void handleMessage(Message msg) { SampleActivity activity = (SampleActivity )mActivityReference.get(); if (activity != null) { // ... } } public void release() { removeCallbacksAndMessages(null); mActivityReference.clear(); } } private final MyHandler mHandler = new MyHandler(this);
在Activity的OnDestroy中调用
public void onDestroy() { mHandler.release(); }
1.3 Handler替代方案
有些时候,Handler无需本身定义,咱们可使用View自身提供的Handler进行操做
myView.post()
myView.postDelay()
myView.getHandler()
runOnUiThread()
.............................
LocalBroadcastReceiver
BroadcastReceiver
或者使用开源方案EventBus
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2.1 避免全局static化
对于广播,或者一些Runnable,CallBacks甚至普通内部类,咱们要避免全局化,致使变量不能被释放,从而对象也不能释放
这里所说的全局化是被静态对象持有,好比单例,全局静态List,Map等
2.2全局化每每是容易持有对象,所以咱们必须学会
add——remove
addAll——clearAll
register——unregister
bind——unbind
这里相关的主要有View,ViewTreeObserver,ContentObserver,static Map,static List, static SparseArray,singleObject如
View.addOnAttachStateChangeListener View.addOnLayoutChangeListener ViewTreeObserver.add registerBroadcast bindService registerContentObserver
........
2.3若是全局化对象非要持有Context对象
建议使其持有ApplicationContext对象,而不是Service,BroadCastReceiver或者Activity
举个栗子
android.support.v4.content.LocalBroadcastManager的getInstance
private static LocalBroadcastManager mInstance; public static LocalBroadcastManager getInstance(Context context) { if(mInstance == null) mInstance = new LocalBroadcastManager(context.getApplicationContext()); return mInstance; }
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Android中图片有四种属性,分别是:
ALPHA_8:每一个像素占用1byte内存
ARGB_4444:每一个像素占用2byte内存
ARGB_8888:每一个像素占用4byte内存 (默认)
RGB_565:每一个像素占用2byte内存
Android默认的颜色模式为ARGB_8888,这个颜色模式色彩最细腻,显示质量最高。但一样的,占用的内存也最大。 因此在对图片效果不是特别高的状况下使用RGB_565(565没有透明度属性)
publicstaticBitmapreadBitMap(Contextcontext, intresId) { BitmapFactory.Optionsopt = newBitmapFactory.Options(); opt.inPreferredConfig = Bitmap.Config.RGB_565; opt.inPurgeable = true; opt.inInputShareable = true; //获取资源图片 InputStreamis = context.getResources().openRawResource(resId); returnBitmapFactory.decodeStream(is, null, opt); } // 先判断是否已经回收 if(bitmap != null && !bitmap.isRecycled()){ // 回收而且置为null bitmap.recycle(); bitmap = null; }
3.1Bitmap调用的时候有个方法是recycle,用来释放内存
3.2使用SoftReference<Bitmap>,WeakReference<Bitmap>进行小内存引用管理
3.3创建磁盘缓存LruDisk,LruCache,具体须要Http 304文件校验,Http 206断点续传等方面知识
3.4进行图片压缩,具体参考http://my.oschina.net/ososchina/blog/495861
具体用法请参考http://my.oschina.net/ososchina/blog/355721
问题一,主类释放问题:内部类隐式持有主类的当前对象,对于内部类需不须要和Handler同样进行静态化,彻底取决于内部类会不会被全局(static/单例)对象引用,若是被全局(static/单例)对象引用可能形成没法释放,不然彻底不要过于担忧,由于内部类对主类对象是强引用。
问题二.循环引用问题:
(所谓循环引用是2个货2个以上的相互关联的类的对象互相引用,形成了一种引用闭环问题,这种引用形成的问题是闭环内的全部对象都没法及时销毁)
Java的虚拟机机制避免了此类问题,可是为了App性能,咱们须要特别注意此类问题,尽量提早释放内存(release memory)。
class MyAdapter extends BaseAdapter { private ListView listView;//用于接收传递过来的Context对象 public MyImgAdapter(ListView listView) { super(); this.listView = listView; this.listView.setAdapter(this); //从这里开始出现循环引用 } @Override public int getCount() { return imgs.length; } @Override public Object getItem(int position) { return position; } @Override public long getItemId(int position) { return position; } @Override public View getView(int position, View convertView, ViewGroup parent) { return convertView; } public void release() { //如下2句选择一种便可解除循环引用,可是为了效率,都用吧 this.listView.setAdapter(null); this.listView = null; } }
参考:Android postDelay+Dialog引发的窗体泄露
findViewById自己不须要缓存,由于每次都是树形遍历,而LayoutInflater每次都是建立一个新的View,因此必要时使用ViewHolder或者ViewModel进行缓存
this.field = null; --------------------------------------------------------------------------------------------------- ViewGroup container= getWindow().getDecorView().findViewById(android.R.id.content); container.removeAllViews(); --------------------------------------------------------------------------------------------------- Iterator it = mImageList.iterator(); while(it.hasNext()) { Bitmap bmp = it.next(); bmp.recycle(); it.remove(); } --------------------------------------------------------------------------------------------------- mList.clear();
11.避免AsyncTask
没法清空队列中的任务、而且不能超过128个任务