首页 > 编程知识 正文

哪些情况会导致内存泄漏(什么是内存泄漏,常见引起引起内存泄漏的原因,及解决办法)

时间:2023-05-03 21:21:24 阅读:123817 作者:579

一:什么是内存泄露

所谓存储器泄漏,也将存储器泄漏称为“存储器泄漏”,是指利用动态存储器分配函数动态打开的区域在使用后没有被释放,其结果是持续占用存储器单元。 直到程序结束。 (其实,说白了,自从该内存空间用完后就没有回收)就是所谓的内存泄漏。

二:常见的内存泄露造成的原因

1、一例存储器泄漏

单个实例的静态特性使其生命周期与应用的生命周期具有相同的长度,因此不需要使用该对象,并且如果单个实例中的对象具有对该对象的引用,则该对象不会被成功重用

例:防止单个例子引起的内存泄漏的例子

//单实例模式公共类管理器{ privatestaticappmanagerinstance; 私有上下文上下文; 隐私应用程序管理器(上下文上下文) { this.context=context; } publicstaticappmanagergetinstance (上下文) if ) instance!=null ) { instance=new appmanager (context ); }返回实例; }2、非静态内部类创建静态实例导致的内存泄漏

例如,在频繁启动的Activity中,为了避免重复创建相同的数据资源,可能会采用以下格式:

publicclassmainactivityextendsappcompatactivity {私有状态测试资源中心资源=空; @ overrideprotectedvoidoncreate (bundlesavedinstancestate ) super.oncreate ) savedinstancestate; setcontentview (r.layout.activity _ main; if(mresource==null ) { mResource=new TestResource; //. } classtestresource {//. } 3、Handler导致的内存泄漏

示例:为匿名内部类创建静态对象

publicclassmainactivityextendsappcompatactivity { privatefinalhandlerhandler=new handler (} { @ overridepublicvoidhandlermession @ overrideprotectedvoidoncreate (bundlesavedinstancestate ) super.oncreate ) savedinstion setcontentview (r.layout.activity _ main; newthread(newrunnable ) ({ @Override public void run ) )//. handler.sendemptymessage (0x 123 ); }; ); }1、从安卓的角度

启动Android APP应用程序时,APP应用程序的主线程会自动创建Looper对象及其关联的消息队列。 在主线程上实例化处理程序对象时,它会自动与主线程的Looper消息队列相关联。 因为发送到消息队列的所有消息都有对Handler的引用,所以Looper会据此回调到Handle的handleMessage () )方法来处理消息。 只要消息队列中有未处理的消息,Looper就会不断从中取出,交给处理程序处理。 此外,主线程的Looper对象还涉及该APP应用程序的整个生命周期。

2、Java角度

在Java中,非静态内部类和匿名类的内部类潜在地保留对它们所属外部类的引用,但不保留静态内部类。

分析上述示例,当MainActivity终止时,未处理的消息具有handler引用,handler具有它所属的外部类,即MainActivity引用。 由于此引用关系将一直保持到处理消息为止,因此不会在垃圾收集器中回收主活动,并且会发生内存泄漏。

解决方案:使Handler类独立或使用静态内部类。 这是

样便可以避免内存泄漏。

4、线程造成的内存泄漏
示例:AsyncTask和Runnable

public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); new Thread(new MyRunnable()).start(); new MyAsyncTask(this).execute(); } class MyAsyncTask extends AsyncTask<Void, Void, Void> { // ... public MyAsyncTask(Context context) { // ... } @Override protected Void doInBackground(Void... params) { // ... return null; } @Override protected void onPostExecute(Void aVoid) { // ... } } class MyRunnable implements Runnable { @Override public void run() { // ... } }}

AsyncTask和Runnable都使用了匿名内部类,那么它们将持有其所在Activity的隐式引用。如果任务在Activity销毁之前还未完成,那么将导致Activity的内存资源无法被回收,从而造成内存泄漏。
解决方法:将AsyncTask和Runnable类独立出来或者使用静态内部类,这样便可以避免内存泄漏。

5、资源未关闭造成的内存泄漏
对于使用了BraodcastReceiver,ContentObserver,File,Cursor,Stream,Bitmap等资源,应该在Activity销毁时及时关闭或者注销,否则这些资源将不会被回收,从而造成内存泄漏。
1)比如在Activity中register了一个BraodcastReceiver,但在Activity结束后没有unregister该BraodcastReceiver。
2)资源性对象比如Cursor,Stream、File文件等往往都用了一些缓冲,我们在不使用的时候,应该及时关闭它们,以便它们的缓冲及时回收内存。它们的缓冲不仅存在于 java虚拟机内,还存在于java虚拟机外。如果我们仅仅是把它的引用设置为null,而不关闭它们,往往会造成内存泄漏。
3)对于资源性对象在不使用的时候,应该调用它的close()函数将其关闭掉,然后再设置为null。在我们的程序退出时一定要确保我们的资源性对象已经关闭。
4)Bitmap对象不在使用时调用recycle()释放内存。2.3以后的bitmap应该是不需要手动recycle了,内存已经在java层了。

6、使用ListView时造成的内存泄漏
初始时ListView会从BaseAdapter中根据当前的屏幕布局实例化一定数量的View对象,同时ListView会将这些View对象缓存起来。当向上滚动ListView时,原先位于最上面的Item的View对象会被回收,然后被用来构造新出现在下面的Item。这个构造过程就是由getView()方法完成的,getView()的第二个形参convertView就是被缓存起来的Item的View对象(初始化时缓存中没有View对象则convertView是null)。
构造Adapter时,没有使用缓存的convertView。
解决方法:在构造Adapter时,使用缓存的convertView。

7、集合容器中的内存泄露
我们通常把一些对象的引用加入到了集合容器(比如ArrayList)中,当我们不需要该对象时,并没有把它的引用从集合中清理掉,这样这个集合就会越来越大。如果这个集合是static的话,那情况就更严重了。
解决方法:在退出程序之前,将集合里的东西clear,然后置为null,再退出程序。

8、WebView造成的泄露
当我们不要使用WebView对象时,应该调用它的destory()函数来销毁它,并释放其占用的内存,否则其长期占用的内存也不能被回收,从而造成内存泄露。
解决方法:为WebView另外开启一个进程,通过AIDL与主线程进行通信,WebView所在的进程可以根据业务的需要选择合适的时机进行销毁,从而达到内存的完整释放。

三:如何避免内存泄漏?

1、平常养成良好的代码书写习惯,该销毁的对象要销毁比如destory啊 广播啊 ,涉及到要用到content上下文的优先考虑全局上线文对象。

版权声明:该文观点仅代表作者本人。处理文章:请发送邮件至 三1五14八八95#扣扣.com 举报,一经查实,本站将立刻删除。