b2科目四模拟试题多少题驾考考爆了怎么补救
b2科目四模拟试题多少题 驾考考爆了怎么补救

解决办法:内存泄漏的常见原因和解决方法

电脑杂谈  发布时间:2020-11-02 10:02:53  来源:网络整理

ndk 常见内存泄漏_ndk 常见内存泄漏

内存泄漏,内存泄漏)

为什么会有内存泄漏?

当不再需要使用某个对象并应对其进行回收时,正在使用的另一个对象将保留其引用,以便无法对其进行回收,这导致本应回收的对象无法被回收并留在堆中,这会导致内存泄漏。

内存泄漏对程序的影响?

内存泄漏是应用程序中OOM的主要原因之一。我们知道,Android系统为每个应用程序分配的内存是有限的,并且当应用程序中有更多的内存泄漏时,这将不可避免地导致应用程序所需的内存超过系统分配的内存配额,从而导致内存不足溢出会导致应用程序崩溃。

如何检查和分析内存泄漏?

由于内存泄漏在堆内存中,因此我们看不到它。通常我们可以使用MAT,LeakCanary等工具来检测应用程序是否存在内存泄漏。

1、MAT是一种功能强大且具有许多复杂功能的内存分析工具。

2、LeakCanary是由Square开源的轻量级第三方内存泄漏检测工具。当在程序中检测到内存泄漏时,它将以最直观的方式告诉我们发生内存泄漏的位置。谁造成了泄漏,无法回收。

常见内存泄漏和解决方法1、由单例导致的内存泄漏

由于单例的静态特性,其生命周期与应用程序的生命周期一样长。如果不再需要某个对象,而单例对象仍保留对该对象的引用,则该对象将无法进行“正常”恢复,从而导致内存泄漏。

示例:防止单例导致内存泄漏。

ndk 常见内存泄漏_ndk 常见内存泄漏

// 使用了单例模式
public class AppManager {
    private static AppManager instance;
    private Context context;
    private AppManager(Context context) {
        this.context = context;
    }
    public static AppManager getInstance(Context context) {
        if (instance != null) {
            instance = new AppManager(context);
        }
        return instance;
    }
}

通过这种方式,无论传入什么上下文,最终都将使用应用程序上下文,并且单例的生命周期与应用程序一样长,这可以防止内存泄漏。 ? ? ?

2、由于创建非静态内部类的静态实例而导致的内存泄漏

例如,有时我们可能会频繁地在Activity中开始,为避免重复创建相同的数据资源,可能会显示以下内容:

public class MainActivity extends AppCompatActivity {
    private static TestResource mResource = null;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        if(mResource == null){
            mResource = new TestResource();
        }
        //...
    }
    
    class TestResource {
    //...
    }
}

通过这种方式,在Activity内部创建了一个非静态内部类的单例,并且每次Activity启动时都会使用该单例的数据。尽管这样可以避免重复创建资源,但是这种写入方式将导致内存泄漏。因为默认情况下非静态内部类将保存外部类的引用,并且非静态内部类创建静态实例,所以实例的生命周期与应用程序一样长,这导致静态实例始终保持活动的引用,这将导致活动的内存资源无法正常恢复。

解决方案:将内部类设置为静态内部类,或提取内部类并将其封装为单例。如果需要使用上下文,请使用应用程序上下文。

3、处理程序引起的内存泄漏

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

public class MainActivity extends AppCompatActivity {
    private final Handler handler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            // ...
        }
    };
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        new Thread(new Runnable() {
            @Override
            public void run() {
                // ...
                handler.sendEmptyMessage(0x123);
            }
        });
    }
}

1、从Android的角度来看

启动Android应用程序时,该应用程序的主线程将自动创建Looper对象及其关联的MessageQueue。在主线程中实例化Handler对象时,该对象将自动与主线程Looper的MessageQueue关联。发送到MessageQueue的所有Messag都将保留对Handler的引用,因此Looper将相应地回调Handle的handleMessage()方法以处理消息。只要MessageQueue中有未处理的消息,Looper就会继续将它们取出并交给处理程序进行处理。另外,主线程的Looper对象将伴随应用程序的整个生命周期。

ndk 常见内存泄漏_ndk 常见内存泄漏

2、Java观点

在Java中,非静态内部类和匿名内部类都可能持有对它们所属的外部类的引用,但是静态内部类则不然。

分析上面的示例,当MainActivity结束时,未处理的消息保存对处理程序的引用,而处理程序保存对它所属的外部类MainActivity的引用。该引用关系将一直保持到消息被处理为止,这将防止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 {
        // ...
        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)例如,在活动中注册了BraodcastReceiver,但活动结束后并未注销BraodcastReceiver。

ndk 常见内存泄漏_ndk 常见内存泄漏

2)游标,流,文件文件等资源对象经常使用某些缓冲区。不使用它们时,应及时关闭它们,以便它们的缓冲区可以及时回收内存。它们的缓冲区不仅存在于Java虚拟机中,而且还存在于Java虚拟机之外。如果我们仅将其引用设置为null而不关闭它们,则通常会导致内存泄漏。

3)当不使用资源对象时,应调用其close()函数以将其关闭,然后将其设置为null。当程序退出时,我们必须确保资源对象已关闭。

4)当不使用位图对象时,调用recycle()释放内存。2.3之后的位图不需要手动回收,内存已经在java层中。

6、使用ListView时发生内存泄漏

首先,ListView将根据当前屏幕布局从BaseAdapter实例化一定数量的View对象,并且ListView将缓存这些View对象。当ListView向上滚动时,最初位于顶部的Item的View对象将被回收,然后用于构造在下面新显示的Item。此构造过程由getView()方法完成。 getView()的第二个参数convertView是要缓存的Item的View对象(初始化期间如果缓存中没有View对象,则convertView为null)。

构造适配器时,不使用缓存的convertView。

解决方案:构造适配器时,请使用缓存的convertView。

7、收集容器中的内存泄漏

我们通常将某些对象的引用添加到集合容器(例如ArrayList)。当我们不需要对象时,我们不会从集合中清除其引用,因此集合将变得越来越大。如果此集合是静态的,情况将更加严重。

解决方案:退出程序之前,清除集合中的项目,然后将其设置为null,然后退出程序。

8、WebView引起的泄漏

当我们不使用WebView对象时,应该调用它的destroy()函数来销毁它并释放它所占用的内存,否则它长时间占用的内存将无法回收,从而导致内存泄漏。

ndk 常见内存泄漏_ndk 常见内存泄漏

解决方案:为WebView打开另一个进程,通过AIDL与主线程进行通信,可以根据业务需要在适当的时候销毁WebView所在的进程,以实现内存的完全释放。

如何避免内存泄漏?

1、在使用上下文时,应将“应用程序上下文”用于生命周期比“活动”更长的对象。在使用上下文优先于应用程序上下文的情况下,它当然不是灵丹妙药。对于某些地方,必须使用活动上下文。应用,服务和活动上下文的应用场景如下:

ndk 常见内存泄漏

其中,NO1表示应用程序和服务可以启动活动,但是需要创建新的任务任务队列。对于对话框,只能在活动中创建。这三个都可以使用。

2、由于需要在静态内部类中使用非静态外部成员变量(例如:Context,View),因此可以在静态内部类中使用弱引用来引用外部类变量,以避免内存泄漏。

3、对于不再需要的对象,将其显式分配为null,例如在使用Bitmap之后调用recycle(),然后将其分配为null。

4、对对象的生命周期保持敏感,特别注意单例,静态对象,全局集合等的生命周期。

5、对于生命周期比“活动”更长的内部类对象,在内部类中使用外部类的成员变量,可以这样做以避免内存泄漏:

1)将内部类更改为静态内部类

2)在静态内部类中使用弱引用来引用外部类的成员变量

参考

Android内存泄漏摘要


本文来自电脑杂谈,转载请注明本文网址:
http://www.pc-fly.com/a/shoujiruanjian/article-330340-1.html

    相关阅读
      发表评论  请自觉遵守互联网相关的政策法规,严禁发布、暴力、反动的言论

      • 任李彬
        任李彬

        >我自愿为国家尊严而战

      • 田悼子
        田悼子

        老个JJ新闻说啥你就以为啥

      • 方信孺
        方信孺

        自始至终都是日舰比北洋多

      热点图片
      拼命载入中...