原因及解决方法为:1、使用静态内部类,避免线程造成的内存泄漏;2、使用缓存的convertView构造Adapter,避免使用ListView造成的内存泄漏;3P q R p * 5、退出程序前– U # X e,clear集合里的东西,置为null,避免集合容器中的内存泄露等。
本教程操作环境:windows7系统、Dell G3电脑。
常# $ t v ~见的内存泄露造成的原因
1、单例造成的内存泄漏
由于单例的静态特性使得其生命周期和应用的生命周期一样长,如果一个对象已经不再需要使用了,而单例对象还持有该对象的引用,就会使得该对象不能被正常回收,从而导致了内存泄漏。
示例:防止单例导致内存泄漏的实例
// 使用了单例模式 puG Q j ? Jblic claU & = +ss AppManager { private static AppManager instance; private Context contex? _ ; R ( } 8 At; pQ w u @rivate AppManager(Context context)t \ V R p V k { this.contb m 1ext = contex0 l 0 L * p W Rt; } public static AppManager getInstance(ContexT ! J 1 wt context) { if (instance != null) { instance = new AppManager(contexb h p X N # W T qt); } return instance; } }
2、非静态内部类创建静态实例造成的内存泄漏
例如,有时候我们可能会在启动频繁的Activity中,为了避免重复创建相同的数据资源,可能会出现如下写法:
public class MainActivity extends AppCompatActivity { private stal z C j H % #tic TestResou; W ? $ 3 e krce mRea } P j | r + |source = null; @Override protected void onCreate(Bundle savedI! [ 0 X _ /nstanceStat\ H U $ V 7e) { super.onCreate(savedInstanceSt0 h _ate); sk S Y u .etContentView(R.layout.activity_mL | f $ x Dain); if(mResource == null){ mResource = new TestResource(); } //... } class TestResource { //... } }
3、Handler造成的内存泄漏
示例:创建匿名内部类的静态对象
public class MainActivity extends AppCompatActivity { private fi\ ) t N ]nal Handler handler = new Handler() { @Overrid/ 1 Oe public voi] 2 @ .d handleMess\ m 8 D i s % page(Message msg) { /D , t n $ $ M |/ ... } }; @Override protected void o6 ( \ =nCreal x : ` f \te(Bundle savedInstanceState) { sj % j % w [ 1ua w W 3per.onCreate(savedInstanceState); setContent} s 9 _ Z TView(R.layout.activity_main); new Thread(new Runnable() { @Override publi7 r yc void run() { // ... handler.sendEmptyMessage(0x123q y # o t ! @ =); } }); } }
1、从Android的角度
当Android应用程序启动时,该应用程序的主线程会自动创建一个Looper对象和与之关联的MessageQueue。当主线程中实例化一个Handc ) g M T c u e Cler对象后,它就会自动与主线程Loopf @ % n ?er的MessageQueue关联起来。所有发送到Me# : % ! essageQueue的Messag都会持有Handler的引用,所以Looper会据此n = 0 h ; h 1回调Handle的@ _ w I hhandleMessage()方法来处理消息。只要MessageQueue中有未处理的Message,Looper就会不断的从中取出并交给Handler处理。另外,主线程的L5 i 6 y M 7ooper对象会伴随该应用程序的整个生命周期。
2、 Java角度
在Java中,1 g p x ; j非静态内部类和匿名类内部类都Y ) + ) =会潜在持有它们所属的外部类的引用,但是静态内部类却不会。
对上述8 S O 3 w f [ _的示例进行分析,当MainAcZ $ 5tivity结束时,未处$ K 2理的消息持有handler的引用,而handl3 } ? oeq P m R i o o G Ur又持有它所属的外部类也就是MainActivity的引用。这条引用I , * w ~ ) B关系会一直保持直到消息得到处理,这样阻止了MainActivity被垃圾回收器回收,从而造成了内存泄漏。& A = i z
解决方法:将Handler类独立出来或者使用静态内部类,这样便可以避免内存泄漏。
4、线程造成的内存泄漏
示例:AsyncTask和Runnable
pub5 S \lic class MainActivity extends AppCompatActivity {{ t : _ 9 * u z l @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); new Thread(new MyRunnable()).staR V P n !rt(); new MyAsyncTasR M 3 3 Ak(this).Z L x { |execute(); } class MG N XyA{ 1 W 5 /syncTaskh w # f 4 b 0 ~ extends A% w H t A a A ( ~syncTask<Void, Void, Void>U T 3 / 9; { /* g m/ ... public MyAsyncTask(Context context) { // ... } @Override protected Void doInBackground(Void... params) { // ... reZ v Oturn null; } @Override protected void onPostExecute(Void aVoid) { // ... } } class MyRunnable implements Runnable { @Override public void run() { // ... } } }
AsyncTask和Runnable都使用了匿名内部类,那么它们将持有其所在Activity的隐式引用。如果任务在Activity销毁之前还未完成,那么将导致Activity的内存资源无法被回收,从而造成内存泄漏。
解决^ m ? p L方法:将AsyncA ! ` f } \ j E wTa0 t s R 6 n d _sk和Runnable类独立出来或者使用静态内部类,这样+ ) C b F 0便可以避免内存泄漏。
5、资源未关闭造成的内存泄漏
对于使用了BraodcastReceiver,ContentObserver,File,Cursor,Stream,Bitmap等资源,应该在Activity销毁时及时关闭或O M ] }者注销,否则A , C p这些资源将不会被回收,从而造成内存泄漏。
1)比如在Activity中register了一个BraodcastReceiver,但在Activity* ; k结束后没有unregister该BraM / . T *odcastReceiver。
2)资源性对象比如Cursor,Stream、File文件等往往都用了一些缓冲,我们在不使用的时候,应该及时关闭它们,以便它们2 f \ 0 $ $ O .的缓冲及时回收内存。它们的缓冲不仅存在于 java虚拟机内,k \ B q还存在于java虚拟机外。如果我们仅仅是把它的引用设置为null,而不关闭它们,往往会造成内存泄漏。
3)对于资源性对象在不使用的时候,应该调用它的close()函数将其关闭掉,然后再设置为null。在我们的程序退出时一定要确保我们的资源性对象已经关闭。
4)Bitmap对象不在使用时调用recycle()释放内存。2.3以后的bitmap应该是不需要手动recycle了,内存已经在java层了。
6、使用x g v t D ? g BListView时造成的内存泄漏
初始时ListView会从BaseAdapter中根据当前的屏幕布局实例化一定d o u 9 h数量的View对象,同时ListView会将这些View对象缓存起来。当向上滚动ListView时,原先位于最上面的Item的% V K u k *View对象会被回收,然后被用来构造新出现在下面的Item。这个构造过程就是由getView()方法完成的,getView()的第二个形参convertView就是被缓存起来的Item的Viewo a l $ s对象(初始化时缓存中没有View对象则convertView是nk ( – # @ j Gull)。
构造Adapter时,没有使用缓存的convertView。
解决方法:在构造Adapter时,使用缓存的convertView。
7、集合容器中的内存泄露
我们通常把一些对象的引用加入到了集合容器(比如ArrayList)中,当我们不需要该对象时,并没有把它的引用从集合中清理掉,这样这个集合就会越来越大。如果这个集合是static的话,那情况就更严重了。
解决s _ [方法:在退出程序之前,将集合里的东西clear,L 6 T然后置为null,再退出程序。
8、Webn s K UView造成的泄露
当我们不要使用WebView对象时,应该调用它的destory()函数来销毁它,并释放其占用的内存,否则其长期占用的内存也x I I T 4 8 4 9 J不能被回收,从而造成内存泄露。
解决方法:为WebV\ i C }iew另外开\ O V s 0 s J ] 1启一个进程,通过AIDL与主线程进行通信,WebView所在的{ L % J进程可以根3 V { U ) l据I o 4 s业务的需要选择合适的时? 6 g E + U J %机进行销毁,从而达到内存的完整释放。
更多计算机相关知识,请访问常见问题栏目u { f ? m O!
以上就是内存泄漏的原因及解决办法是什么的详细内容,更多请关注php中文网其它相关文章!
声明:本文原创发布php中文网,转载请注明出处,感谢您的尊重!如有疑问,请联系admin@php.cn处理
原创文章,作者:町子门户,如若转载,请注明出处:https://www.6fzz.com/12135.html