文章目录内存泄漏静态变量引用Activitystatic间接修饰Activity单例引用Context匿名内部类执行耗时任务非静态内部类Handler引起的内存泄漏资源对象没有关闭OOM
内存泄漏静态变量引用Activity
静态变量引用Activty对象时,会导致Activty对象所占内存内漏。
原因:静态变量是驻扎在JVM的方法区,因此,静态变量引用的对象是不会被GC回收的,因为它们所引用的对象本身就是GCROOT,即最终导致Activity对象不被回收,从而也就造成内存泄漏。
常见的错误用法:
将Context或Activity赋值给某个静态变量
解决:
去掉static关键字,使用别的方法来实现想要的功能。在onDestroy方法中置空Activity静态引用也可以使用到软引用解决,确保在Activity销毁时,垃圾回收机制可以将其回收。static间接修饰Activity
有时,当一个Activity经常启动,但是对应的View读取非常耗时,我们可以通过静态View变量来保持对该Activity的rootView引用。这样就可以不用每次启动Activity都去读取并渲染View了。这确实是一个提高Activity启动速度的好方法!但是要注意,一旦Viewattach到我们的Window上,就会持有一个Context(即Activity)的引用。而我们的View有事一个静态变量,所以导致Activity不被回收。当然了,也不是说不能使用静态View,但是在使用静态View时,需要确保在资源回收时,将静态Viewdetach掉。
常见问题
publicclassLoadingDialogextendsDialog{privatestaticLoadingDialogmDialog;privateTextViewmText;}
这里static虽然没有直接修饰TextView(拥有Context引用),但是修饰了mDialog成员变量,mDialog是一个LoadingDialog对象,LoadingDialog对象包含一个TextView类型的成员变量,所以mText变量的生命周期也是全局的,和应用一样。这样,mText持有的Context对象销毁时,没有GC回收,导致内存泄露。
解决:
不使用static修饰在适当的地方置空mDialog单例引用ContextpublicclassAppManager{privatestaticAppManagerinstance;privateContextcontext;privateAppManager(Contextcontext){this.context=context;}publicstaticAppManagergetInstance(Contextcontext){if(instance==null){instance=newAppManager(context);}returninstance;}}
AppManager是一个单例类,他需要Context作为成员变量,Context如果是ActivityContext的话,必然会引起内存泄漏。
解决
使用ApplicaionContext代替ActivityContext在调用的地方使用弱引用匿名内部类执行耗时任务publicclassMainActivityextendsActivity{@OverrideprotectedvoidonCreate(BundlesavedInstanceState){super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);test();}publicvoidtest(){newThread(newRunnable(){@Overridepublicvoidrun(){while(true){try{Thread.sleep(1000);}catch(InterruptedExceptione){e.printStackTrace();}}}}).start();}}
原因
非静态内部类或匿名类会拥有所在外部类的引用,newThread是匿名内部类,它一直在执行当Activity消耗后,该匿名内部类还在执行任务,导致外部的Activity不能回收。
解决
静态匿名内部类,使用static修饰publicstaticvoidtest(){newThread(newRunnable(){@Overridepublicvoidrun(){while(true){try{Thread.sleep(1000);}catch(InterruptedExceptione){e.printStackTrace();}}}}).start();}非静态内部类
如果一个变量,既是静态变量,而且是非静态的内部类对象,那么也会造成内存泄漏。
publicclassLeakActivityextendsAppCompatActivity{privatestaticHellosHello;@OverrideprotectedvoidonCreate(BundlesavedInstanceState){super.onCreate(savedInstanceState);setContentView(R.layout.activity_leak);sHello=newHello();}publicclassHello{}}
这里我们定义的Hello虽然是空的,但它是一个非静态的内部类,所以它必然会持有外部类即LeakActivity.this引用,导致sHello这个静态变量一直持有这个Activity,Activity无法被回收。
Handler引起的内存泄漏
这与非静态匿名内部类执行耗时任务一样,错误代码如下。
publicclassMainActivityextendsActivity{privatefinalHandlermLeakyHandler=newHandler(){@OverridepublicvoidhandleMessage(Messagemsg){//...}}@OverrideprotectedvoidonCreate(BundlesavedInstanceState){super.onCreate(savedInstanceState);mLeakyHandler.postDelayed(newRunnable(){@Overridepublicvoidrun(){//...}},1000*60*10);finish();}}
在活动中发送了一个延时十分钟消息的message,handler会将消息放入MessageQueue里面。当活动被finish()时,Message还会继续存在于主线程,Handler是非静态内部类,会持有该活动的引用,所以此时finish()掉的活动就不会回收。
解决方法:
自定义静态Handler
publicclassMainActivityextendsActivity{privatefinalMyHandlermHandler=newMyHandler(this);privatestaticfinalRunnablemRunnable=newRunnable(){@Overridepublicvoidrun(){/*...*/}};@OverrideprotectedvoidonCreate(BundlesavedInstanceState){super.onCreate(savedInstanceState);mHandler.postDelayed(mRunnable,1000*60*10);finish();}privatestaticclassMyHandlerextendsHandler{privatefinalWeakReference<MainActivity>mActivityReference;publicMyHandler(MainActivityactivity){mActivityReference=newWeakReference<MainActivity>(activity);}@OverridepublicvoidhandleMessage(Messagemsg){MainActivityactivity=mActivityReference.get();if(activity!=null){//...}}}}
解释:
声明一个静态的MyHandler类,他不会隐式的持有MainActivity的引用内部利用弱引用获取外部类的引用,若Activity回收,弱引用不会影响Activity的回收资源对象没有关闭
资源性对象比如(Cursor,File文件等)往往都用了一些缓冲,我们在不使用的时候,应该及时关闭它们,以便它们的缓冲及时回收内存。它们的缓冲不仅存在于java虚拟机内,还存在于java虚拟机外。如果我们仅仅是把它的引用设置为null,而不关闭它们,往往会造成内存泄漏。
OOM
原因
内存泄漏导致,频繁的内存泄漏将会引发内存溢出。占用内存较多的对象,保存了多个耗用内存较多的对象(如Bitmap),加载超大的图片。
加载图片OOM处理
等比例缩小图片使用setImageBitmap或setImageResource或BitmapFactory.decodeResource设置大图时,这些函数在完成decode后,最终都是通过java层的createBitmap来完成的,需要消耗更多内存。publicstaticBitmapscaleImage(Bitmapbitmap,intnewWidth,intnewHeight){if(bitmap==null){returnnull;}floatscaleWidth=(float)newWidth/bitmap.getWidth();floatscaleHeight=(float)newHeight/bitmap.getHeight();Matrixmatrix=newMatrix();matrix.postScale(scaleWidth,scaleHeight);returnBitmap.createBitmap(bitmap,0,0,bitmap.getWidth(),bitmap.getHeight(),matrix,true);}对图片采用软引用,及时地进行recycle()操作。虽然系统能够确认Bitmap分配的内存最终会被销毁,但是由于它占用的内存过多,所以很可能会超过java堆的限制。因此,在用完Bitmap时要及时的recycle掉。recycle并不能确定立即就会将Bitmap释放掉,但是会给虚拟机一个暗示:“该图片可以释放了”。SoftReference<Bitmap>bitmap;bitmap=newSoftReference<>(pBitmap);if(bitmap!=null){if(bitmap.get()!=null&&!bitmap.get().isRecycled()){bitmap.get().recycle();bitmap=null;}}
其他OOM可使用LeakCanary检测是否发生了内存泄漏。