javaee论坛

普通会员

225648

帖子

355

回复

369

积分

楼主
发表于 2019-11-03 06:52:14 | 查看: 130 | 回复: 1

文章目录内存泄漏静态变量引用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检测是否发生了内存泄漏。


普通会员

0

帖子

218

回复

220

积分
沙发
发表于 2024-05-08 00:21:26

如果你智商能再高点,也许我会上当

您需要登录后才可以回帖 登录 | 立即注册

触屏版| 电脑版

技术支持 历史网 V2.0 © 2016-2017