内存泄漏解决_解决闭包内存泄漏 - CSDN
精华内容
参与话题
  • 常见的内存泄漏解决方式

    千次阅读 2018-08-01 15:25:24
    什么是内存泄漏?  有些对象只有有限的生命周期。当它们的任务完成之后,它们将被垃圾回收。如果在对象的生命周期本该结束的时候,这个对象还被一系列的引用,这就会导致内存泄漏。随着泄漏的累积,app将消耗完...

    什么是内存泄漏?

        有些对象只有有限的生命周期。当它们的任务完成之后,它们将被垃圾回收。如果在对象的生命周期本该结束的时候,这个对象还被一系列的引用,这就会导致内存泄漏。随着泄漏的累积,app将消耗完内存。

    内存泄漏造成什么影响?

        它是造成应用程序OOM的主要原因之一。由于android系统为每个应用程序分配的内存有限,当一个应用中产生的内存泄漏比较多时,就难免会导致应用所需要的内存超过这个系统分配的内存限额,这就造成了内存溢出而导致应用Crash。

    什么是LeakCanary?

          leakCanary是Square开源框架,是一个Android和Java的内存泄露检测库,如果检测到某个 activity 有内存泄露,LeakCanary 就是自动地显示一个通知,所以可以把它理解为傻瓜式的内存泄露检测工具。通过它可以大幅度减少开发中遇到的oom问题,大大提高APP的质量。

         本篇不介绍如何使用LeakCanary,关于LeakCanary使用参考以下文章:

         1.LeakCanary: 让内存泄露无所遁形 
         2.LeakCanary 中文使用说明

    LeakCanary捕获常见内存泄漏以及解决办法

    1.)错误使用单例造成的内存泄漏

         在平时开发中单例设计模式是我们经常使用的一种设计模式,而在开发中单例经常需要持有Context对象,如果持有的Context对象生命周期与单例生命周期更短时,或导致Context无法被释放回收,则有可能造成内存泄漏,错误写法如下:

    复制代码

    public class LoginManager {
        private static LoginManager mInstance;
        private Context mContext;
    
        private LoginManager(Context context) {
            this.mContext = context;
        }
    
    
        public static LoginManager getInstance(Context context) {
            if (mInstance == null) {
                synchronized (LoginManager.class) {
                    if (mInstance == null) {
                        mInstance = new LoginManager(context);
                    }
                }
            }
            return mInstance;
        }
    
        public void dealData() {
        }
    
    }

    复制代码

    弱我们在一个Activity中调用的,然后关闭该Activity则会出现内存泄漏。

    LoginManager.getInstance(this).dealData();

    LeakCanary检测结果如下:

     解决 办法要保证Context和AppLication的生命周期一样,修改后代码如下:

    复制代码

    public class LoginManager {
        private static LoginManager mInstance;
        private Context mContext;
    
        private LoginManager(Context context) {
            this.mContext = context.getApplicationContext();
        }
    
    
        public static LoginManager getInstance(Context context) {
            if (mInstance == null) {
                synchronized (LoginManager.class) {
                    if (mInstance == null) {
                        mInstance = new LoginManager(context);
                    }
                }
            }
            return mInstance;
        }
    
        public void dealData() {
        }
    
    }

    复制代码

    2.)Handler造成的内存泄漏

       早些年Handler的使用频率还是蛮高的,它是工作线程与UI线程之间通讯的桥梁,只是现在大量开源框架对其进行了封装,我们这里模拟一种常见使用方式来模拟内存泄漏情形。

    复制代码

    public class MainActivity extends AppCompatActivity {
        private Handler mHandler = new Handler();
        private TextView mTextView;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            mTextView = (TextView) findViewById(R.id.text);//模拟内存泄露
            mHandler.postDelayed(new Runnable() {
                @Override
                public void run() {
                    mTextView.setText("lcj");
                }
            }, 3 * 60 * 1000);
            finish();
        }
    
        @Override
        protected void onDestroy() {
            super.onDestroy();
            LApplication.getRefWatcher().watch(this);
        }
    }

    复制代码

    上述代码通过内部类的方式创建mHandler对象,此时mHandler会隐式地持有一个外部类对象引用这里就是MainActivity,当执行postDelayed方法时,该方法会将你的Handler装入一个Message,并把这条Message推到MessageQueue中,MessageQueue是在一个Looper线程中不断轮询处理消息,那么当这个Activity退出时消息队列中还有未处理的消息或者正在处理消息,而消息队列中的Message持有mHandler实例的引用,mHandler又持有Activity的引用,所以导致该Activity的内存资源无法及时回收,引发内存泄漏。

    LeakCanary检测结果如下:

      要想避免Handler引起内存泄漏问题,需要我们在Activity关闭退出的时候的移除消息队列中所有消息和所有的Runnable。上述代码只需在onDestroy()函数中调用mHandler.removeCallbacksAndMessages(null);就行了。

    复制代码

    public class MainActivity1 extends AppCompatActivity {
        private Handler mHandler = new Handler();
        private TextView mTextView;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            mTextView = (TextView) findViewById(R.id.text);
            //模拟内存泄露
            mHandler.postDelayed(new Runnable() {
                @Override
                public void run() {
                    mTextView.setText("lcj");
                }
            }, 3 * 60 * 1000);
            finish();
        }
    
        @Override
        protected void onDestroy() {
            super.onDestroy();
            mHandler.removeCallbacksAndMessages(null);
            mHandler=null;
            LApplication.getRefWatcher().watch(this);
        }
    }

    复制代码

     3.)线程造成的内存泄漏

      最早时期的时候处理耗时操作多数都是采用Thread+Handler的方式,后来逐步被AsyncTask取代,直到现在采用RxJava的方式来处理异步。这里以AsyncTask为例,可能大部分人都会这样处理一个耗时操作然后通知UI更新结果:

    复制代码

    public class MainActivity extends AppCompatActivity {
        private AsyncTask<Void, Void, Integer> asyncTask;
        private TextView mTextView;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            mTextView = (TextView) findViewById(R.id.text);
            testAsyncTask();
            finish();
        }
    
        private void testAsyncTask() {
            asyncTask = new AsyncTask<Void, Void, Integer>() {
                @Override
                protected Integer doInBackground(Void... params) {
                    int i = 0;
                    //模拟耗时操作
                    while (!isCancelled()) {
                        i++;
                        if (i > 1000000000) {
                            break;
                        }
                        Log.e("LeakCanary", "asyncTask---->" + i);
                    }
                    return i;
                }
    
                @Override
                protected void onPostExecute(Integer integer) {
                    super.onPostExecute(integer);
                    mTextView.setText(String.valueOf(integer));
                }
            };
            asyncTask.execute();
    
        }
    
        @Override
        protected void onDestroy() {
            super.onDestroy();
            LApplication.getRefWatcher().watch(this);
        }
    
    }

    复制代码

    对于上面的例子来说,在处理一个比较耗时的操作时,可能还没处理结束MainActivity就执行了退出操作,但是此时AsyncTask依然持有对MainActivity的引用就会导致MainActivity无法释放回收引发内存泄漏。

    LeakCanary检测结果:

    如何解决这种内存泄漏呢?在使用AsyncTask时,在Activity销毁时候也应该取消相应的任务AsyncTask.cancel()方法,避免任务在后台执行浪费资源,进而避免内存泄漏的发生。

    复制代码

    public class MainActivity3 extends AppCompatActivity {
        private AsyncTask<Void, Void, Integer> asyncTask;
        private TextView mTextView;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            mTextView = (TextView) findViewById(R.id.text);
            testAsyncTask();
            finish();
        }
    
        private void testAsyncTask() {
            asyncTask = new AsyncTask<Void, Void, Integer>() {
                @Override
                protected Integer doInBackground(Void... params) {
                    int i = 0;
                    //模拟耗时操作
                    while (!isCancelled()) {
                        i++;
                        if (i > 1000000000) {
                            break;
                        }
                        Log.e("LeakCanary", "asyncTask---->" + i);
                    }
                    return i;
                }
    
                @Override
                protected void onPostExecute(Integer integer) {
                    super.onPostExecute(integer);
                    mTextView.setText(String.valueOf(integer));
                }
            };
            asyncTask.execute();
    
        }
    
        private void destroyAsyncTask() {
            if (asyncTask != null && !asyncTask.isCancelled()) {
                asyncTask.cancel(true);
            }
            asyncTask = null;
        }
    
        @Override
        protected void onDestroy() {
            super.onDestroy();
            destroyAsyncTask();
            LApplication.getRefWatcher().watch(this);
        }
    
    }

    复制代码

     4.)非静态内部类创建静态实例造成的内存泄漏

      有时我们需要一个可以随着屏幕旋转的Activity,比如视频播放Activity,这时我们为了防止多次调用onCreate方法导致某些参数重新初始化,我们一般会选择创建一个内部类和一个静态实例来保存这些参数,比如以下实现:

    复制代码

    public class MainActivity extends AppCompatActivity {
        private static Config mConfig;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            //模拟内存泄露
            if (mConfig == null) {
                mConfig = new Config();
                mConfig.setSize(18);
                mConfig.setTitle("老九门");
            }
            finish();
        }
    
    
        @Override
        protected void onDestroy() {
            super.onDestroy();
            LApplication.getRefWatcher().watch(this);
        }
    
        class Config {
            private int size;
            private String title;
    
            public int getSize() {
                return size;
            }
    
            public void setSize(int size) {
                this.size = size;
            }
    
            public String getTitle() {
                return title;
            }
    
            public void setTitle(String title) {
                this.title = title;
            }
        }
    }

    复制代码

      上述代码看着没有任何问题,其实内部类都会持有一个外部类引用,这里这个外部类就是MainActivity,然而内部类实例又是static静态变量其生命周期与Application生命周期一样,所以在MainActivity关闭的时候,内部类静态实例依然持有对MainActivity的引用,导致MainActivity无法被回收释放,引发内存泄漏。LeakCanary检测内存泄漏结果如下:

    对于这种泄漏的解决办法就是将内部类改成静态内部类,不再持有对MainActivity的引用即可,修改后的代码如下:

    复制代码

    public class MainActivity extends AppCompatActivity {
        private static Config mConfig;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            //模拟内存泄露
            if (mConfig == null) {
                mConfig = new Config();
                mConfig.setSize(18);
                mConfig.setTitle("老九门");
            }
            finish();
        }
    
    
        @Override
        protected void onDestroy() {
            super.onDestroy();
            LApplication.getRefWatcher().watch(this);
        }
    
        static class Config {
            private int size;
            private String title;
    
            public int getSize() {
                return size;
            }
    
            public void setSize(int size) {
                this.size = size;
            }
    
            public String getTitle() {
                return title;
            }
    
            public void setTitle(String title) {
                this.title = title;
            }
        }
    }

    复制代码

     5.)由WebView引起的内存泄漏

     在目前的开发中多多少少会用到Hybrid开发方式,这样我们就会用WebView去承载Html网页,就如下面这种方式:

    java代码:

    复制代码

    public class MainActivity5 extends AppCompatActivity {
        private WebView mWebView;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_web);
            mWebView = (WebView) findViewById(R.id.web);
            mWebView.loadUrl("http://www.cnblogs.com/whoislcj/p/5720202.html");
        }
    
    
        @Override
        protected void onDestroy() {
            super.onDestroy();
            LApplication.getRefWatcher().watch(this);
        }
    
    }

    复制代码

    xml布局文件:

    复制代码

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        android:id="@+id/activity_main"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">
    
        <WebView
            android:id="@+id/web"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"/>
    </LinearLayout>

    复制代码

    WebView解析网页时会申请Native堆内存用于保存页面元素,当页面较复杂时会有很大的内存占用。如果页面包含图片,内存占用会更严重。并且打开新页面时,为了能快速回退,之前页面占用的内存也不会释放。有时浏览十几个网页,都会占用几百兆的内存。这样加载网页较多时,会导致系统不堪重负,最终强制关闭应用,也就是出现应用闪退或重启。及时Activity关闭时在onDestroy中调用如下代码也是没有任何作用。

    复制代码

    private void destroyWebView() {
            if (mWebView != null) {
                mLinearLayout.removeView(mWebView);
                mWebView.pauseTimers();
                mWebView.removeAllViews();
                mWebView.destroy();
                mWebView = null;
            }
        }

    复制代码

    先看下LeakCanary检测到的结果如下:

    该如何解决呢?这个查了不少资料,其中一种就是使用getApplicationgContext作为参数构建WebView,然后动态添加到一个ViewGroup中,最后退出的时候调用webView的销毁的函数,虽然也达到了防止内存溢出的效果,但是在有些网页弹出时候需要记住密码的对话框的时候,会出现Unable to add window -- token null is not for an application 的错误,所以这里采用的解决办法是通过把使用了WebView的Activity(或者Service)放在单独的进程里。然后在检测到应用占用内存过大有可能被系统干掉或者它所在的Activity(或者Service)结束后,调用android.os.Process.killProcess(android.os.Process.myPid());,主动Kill掉进程。由于系统的内存分配是以进程为准的,进程关闭后,系统会自动回收所有内存。

    修改后的代码如下:

    复制代码

    public class MainActivity5 extends AppCompatActivity {
        private WebView mWebView;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_web);
            mWebView = (WebView) findViewById(R.id.web);
            mWebView.loadUrl("http://www.cnblogs.com/whoislcj/p/5720202.html");
        }
    
        @Override
        protected void onDestroy() {
            destroyWebView();
            android.os.Process.killProcess(android.os.Process.myPid());
            super.onDestroy();
            LApplication.getRefWatcher().watch(this);
    
        }
    
        private void destroyWebView() {
            if (mWebView != null) {
                mWebView.pauseTimers();
                mWebView.removeAllViews();
                mWebView.destroy();
                mWebView = null;
            }
        }
    
    }

    复制代码

    manifest中对应的activity配置如下:
    <activity
       android:name=".MainActivity5"
       android:process="com.whoislcj.webview"/>

    6.)资源未关闭造成的内存泄漏

         对于使用了BraodcastReceiver,ContentObserver,File,Cursor,Stream,Bitmap等资源的使用,应该在Activity销毁时及时关闭或者注销,否则这些资源将不会被回收,造成内存泄漏。例如获取媒体库图片地址代码在查询结束的时候一定要调用

    Cursor 的关闭方法防止造成内存泄漏。

    复制代码

         String columns[] = new String[]{
                    MediaStore.Images.Media.DATA, MediaStore.Images.Media._ID, MediaStore.Images.Media.TITLE, MediaStore.Images.Media.DISPLAY_NAME
            };
            Cursor cursor = this.getContentResolver().query(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, columns, null, null, null);
            if (cursor != null) {
                int photoIndex = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA);
                //显示每张图片的地址,但是首先要判断一下,Cursor是否有值
                while (cursor.moveToNext()) {
                    String photoPath = cursor.getString(photoIndex); //这里获取到的就是图片存储的位置信息
                    Log.e("LeakCanary", "photoPath---->" + photoPath);
                }
                cursor.close();
            }

    转载地址:https://www.cnblogs.com/whoislcj/p/6001422.html

    复制代码

    展开全文
  • 常见的内存泄漏原因及解决方法

    千次阅读 2019-02-22 04:54:14
    (Memory Leak,内存泄漏) 为什么会产生内存泄漏? 当一个对象已经不需要再使用本该被回收时,另外一个正在使用的对象持有它的引用从而导致它不能被回收,这导致本该被回收的对象不能被回收而停留在堆内存中,这...

    (Memory Leak,内存泄漏)

    为什么会产生内存泄漏?

    当一个对象已经不需要再使用本该被回收时,另外一个正在使用的对象持有它的引用从而导致它不能被回收,这导致本该被回收的对象不能被回收而停留在堆内存中,这就产生了内存泄漏。

    内存泄漏对程序的影响?

    内存泄漏是造成应用程序OOM的主要原因之一。我们知道Android系统为每个应用程序分配的内存是有限的,而当一个应用中产生的内存泄漏比较多时,这就难免会导致应用所需要的内存超过系统分配的内存限额,这就造成了内存溢出从而导致应用Crash。

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

    因为内存泄漏是在堆内存中,所以对我们来说并不是可见的。通常我们可以借助MAT、LeakCanary等工具来检测应用程序是否存在内存泄漏。
    1、MAT是一款强大的内存分析工具,功能繁多而复杂。
    2、LeakCanary则是由Square开源的一款轻量级的第三方内存泄漏检测工具,当检测到程序中产生内存泄漏时,它将以最直观的方式告诉我们哪里产生了内存泄漏和导致谁泄漏了而不能被回收。

    常见的内存泄漏及解决方法

    1、单例造成的内存泄漏

    由于单例的静态特性使得其生命周期和应用的生命周期一样长,如果一个对象已经不再需要使用了,而单例对象还持有该对象的引用,就会使得该对象不能被正常回收,从而导致了内存泄漏。
    示例:防止单例导致内存泄漏的实例

    // 使用了单例模式
    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;
        }
    }
    

    这样不管传入什么Context最终将使用Application的Context,而单例的生命周期和应用的一样长,这样就防止了内存泄漏。???

    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时都会使用该单例的数据。虽然这样避免了资源的重复创建,但是这种写法却会造成内存泄漏。因为非静态内部类默认会持有外部类的引用,而该非静态内部类又创建了一个静态的实例,该实例的生命周期和应用的一样长,这就导致了该静态实例一直会持有该Activity的引用,从而导致Activity的内存资源不能被正常回收。
    解决方法:将该内部类设为静态内部类或将该内部类抽取出来封装成一个单例,如果需要使用Context,就使用Application的Context。

    3、Handler造成的内存泄漏

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

    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中有未处理的Message,Looper就会不断的从中取出并交给Handler处理。另外,主线程的Looper对象会伴随该应用程序的整个生命周期。
    2、 Java角度
    在Java中,非静态内部类和匿名类内部类都会潜在持有它们所属的外部类的引用,但是静态内部类却不会。

    对上述的示例进行分析,当MainActivity结束时,未处理的消息持有handler的引用,而handler又持有它所属的外部类也就是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<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、在涉及使用Context时,对于生命周期比Activity长的对象应该使用Application的Context。凡是使用Context优先考虑Application的Context,当然它并不是万能的,对于有些地方则必须使用Activity的Context。对于Application,Service,Activity三者的Context的应用场景如下:

     

     

    其中,NO1表示Application和Service可以启动一个Activity,不过需要创建一个新的task任务队列。而对于Dialog而言,只有在Activity中才能创建。除此之外三者都可以使用。

    2、对于需要在静态内部类中使用非静态外部成员变量(如:Context、View ),可以在静态内部类中使用弱引用来引用外部类的变量来避免内存泄漏。
    3、对于不再需要使用的对象,显示的将其赋值为null,比如使用完Bitmap后先调用recycle(),再赋为null。
    4、保持对对象生命周期的敏感,特别注意单例、静态对象、全局性集合等的生命周期。
    5、对于生命周期比Activity长的内部类对象,并且内部类中使用了外部类的成员变量,可以这样做避免内存泄漏:
    1)将内部类改为静态内部类
    2)静态内部类中使用弱引用来引用外部类的成员变量

    参考

    Android内存泄漏总结



    作者:李俊的博客
    链接:https://www.jianshu.com/p/90caf813682d
    來源:简书
    简书著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处。

    展开全文
  • 内存泄露之常见问题解决--初级篇

    千次阅读 2016-03-29 09:05:35
    1.简介在整个Android开发过程中,内存泄露是导致OOM的一个重点因素。大意思就是:GC无法回收原本应该被回收的对象,这个对象就引发了内存泄露。那有什么危害呢?手机的内存大小是有限的,如果不能释放的话,你就无法...

    身为一个段子猿,我决定来写写最近的学习心得。

    1.简介

    在整个Android开发过程中,内存泄露是导致OOM的一个重点因素。大概意思就是:GC无法回收原本应该被回收的对象,这个对象就引发了内存泄露。那有什么危害呢?手机的内存大小是有限的,如果不能释放的话,你就无法创建新的对象,你的新界面等等就无法正常运行,然后程序就OOM了(OutOfMemory)。

    2.OOM以及内存泄露

    OOM通俗点讲就是,你家里有2个厕所,本来你和你老婆用的话,都是够用的,有一天你不小心造人了,从此家里有了1+1=3个人了。一天的凌晨,你起床,发现肚子不舒服,“我要上厕所!”(请求系统分配空余内存给当前app)。咦,老婆你在里面啊,那我去下一家(系统开始分析你需要的内存以及空余内存,准备分配)。啊,儿子你也蹲着啊(完了,2个厕所都被人占着了,内存不足,boom!boom!你此时肯定是奔溃的,拉屎忍不住了,肯定要异常奔溃了。OOM,app程序异常退出了)。

    那么内存泄露呢?比如你买了一堆可擦除的画板(画板=内存条,画板空间有限=内存有限),刚开始你拿去画了一会画,慢慢的用了不少画板。此时你还在继续画,发现没有新的画板了,只好找刚才用过的,但是你发现用错了画笔,导致擦不掉!(不正常的使用context等导致内存泄露了,GC回收不了内存。)而且所以找遍了所有的画板,都没有找到空白的地方(分配不了新的空余内存给app),只好结束此次画画的事情(内存泄露导致内存不足,这个app程序OOM)。

    3.配置

    本篇是初级篇,就不过多描述理论性的东西了,大牛的文章都写过,本人就不再进行描述了。那么我们如何解决内存泄露的问题呢?对于小白的我们肯定想有一款简单易用、快速定位的内存泄露插件,那么我推荐LeakCanary傻瓜式检测工具给大家。

    ok,第一步肯定是怎么在项目里引用这个呢?首先,Android studio的项目引用第三方库的方法你得知道。在build.gradle(下方图里的1位置,2位置不是哦)里如下2个引用。什么,为什么有2个?其实有3个呢,原因很简单,你总不会正式发布的包也加内存泄露吧?当然也可以正式发布时,把相关引用全都干掉。
    这里写图片描述

    dependencies {
        debugCompile 'com.squareup.leakcanary:leakcanary-android:1.3'
        releaseCompile 'com.squareup.leakcanary:leakcanary-android-no-op:1.3'
    }

    引用完了,咱们开始做一些初始化操作。在你的Application的实现类写下下面2句。

    /**
         * 内存泄露检测
         */
        private RefWatcher refWatcher;
        public static RefWatcher getRefWatcher(Context context) {
            TTApplication application = (TTApplication) context.getApplicationContext();
            return application.refWatcher;
        }
      @Override
        public void onCreate() {
            super.onCreate();
            refWatcher = LeakCanary.install(this);//内存泄露检测
    }

    然后在你的fragment的基类里加上这一句

      @Override
        public void onDestroy() {
            RefWatcher refWatcher = TTApplication.getRefWatcher(getActivity());
            refWatcher.watch(this);//内存泄露检测
        }

    好了,我们的配置全部完成了,是不是so easy?先休息一下看个美腿。






    这里写图片描述
    哦,不好意思放错了。
    这里写图片描述

    4.常见内存泄露

    下面就念咒语“哦玛尼玛尼哄”,”奔跑吧我的app”,”出来吧,万恶的内存泄露”。(看下图)
    这里写图片描述

    这里写图片描述
    如果出现内存泄露,上方会有盾牌的通知消息(1位置),点击后可以进入详情界面(2位置)。
    这里写图片描述
    上方的图片里我们能找到一些蛛丝马迹,比如这个回调导致内存泄露了。我回忆了下代码,因为本人这个项目是MVP模式,P层持有activity的context以及IView接口。常见的泄露基本都是上下文的持有导致的,所以应对这一点,我采用P层持有弱引用的方式。弱引用,只具有弱引用的对象拥有更短暂的生命周期。在垃圾回收器线程扫描它所管辖的内存区域的过程中,一旦发现了只具有弱引用的对象,不管当前内存空间足够与否,都会回收它的内存。此外,刚才的回调接口也需要设置null。这样在activity销毁时onDestroy方法里调用下面的destroy方法即可。重要的一点是,网络请求在界面销毁时最好进行cancel操作,比如volly框架的根据请求tag进行取消,这样也是尽量避免接口回调导致的内存泄露。此外,动画在界面离开时或者销毁时,同时进行暂停或者销毁。

    private WeakReference<ShoppingCartActivity> mBaseActivity;
      private IShoppingCartView mIShoppingCartView;
        public ShoppingCartPresenter(ShoppingCartActivity context, IShoppingCartView
                iShoppingCartView) {
            mBaseActivity = new WeakReference<>(context);
            mIShoppingCartView = iShoppingCartView;
        }
         @Override
        public void destroy() {
            mIShoppingCartView = null;
        }

    还有一种常见的就是handler的使用。通常我们直接new一个就完事了,Android studio会显示大大的黄色警告区域。此时就需要利用静态内部类以及弱引用解决了。平常需要context的地方就可以用 mActivity.get()代替了。

    static class CommentHandler extends Handler {
            WeakReference<HomeActivity> mActivity;
    
    
            CommentHandler(HomeActivity activity) {
                mActivity = new WeakReference<>(activity);
            }
    
            public void handleMessage(Message msg) {
                super.handleMessage(msg);
    
            }
        }

    在App不可避免的是webview加载网页了,然后这个也是一个可怕的开始。webview界面内存泄露解决,不要xml设置webview,而是以代码创建view对象的方式进行初始化,并且界面销毁时调用 destroy等方法 。

        WebView  mWebView  =new WebView (this);
    
      @Override
        protected void onDestroy() {
            if (mWebView != null) {
                mWebView.getSettings().setBuiltInZoomControls(true);
                mWebView.setVisibility(View.GONE);// 把destroy()延后
                mWebView.removeAllViews();
                mWebView.destroy();
            }
            super.onDestroy();
        }

    此外,Toast最好用ApplicationContext,如果用activity的context,吐司还没结束的时候退出当前界面,这是内存也会泄露。
    至于context与bitmap之间的点滴,我用了fresco图片加载框架,还算好。而且这方面资料也不少,我暂时也不准备描述了。

    好了,LeakCanary的简单使用就到这里了。下次再来深度分析内存泄露的点点滴滴。

    展开全文
  • 闭包造成内存泄漏问题的解决办法

    千次阅读 2018-09-07 09:28:57
    由于IE的js对象和DOM对象使用不同的垃圾收集方法,因此闭包在IE中会导致内存泄露问题,也就是无法销毁驻留在内存中的元素 function closure(){ var oDiv = document.getElementById('oDiv');//oDiv用完之后一直...

    由于IE的js对象和DOM对象使用不同的垃圾收集方法,因此闭包在IE中会导致内存泄露问题,也就是无法销毁驻留在内存中的元素

    function closure(){
        var oDiv = document.getElementById('oDiv');//oDiv用完之后一直驻留在内存中
        oDiv.onclick = function () {
            alert('oDiv.innerHTML');//这里用oDiv导致内存泄露
        };
    }
    closure();
    //最后应将oDiv解除引用来避免内存泄露
    function closure(){
        var oDiv = document.getElementById('oDiv');
        var test = oDiv.innerHTML;
        oDiv.onclick = function () {
            alert(test);
        };
        oDiv = null;
    }
    展开全文
  • C++内存泄漏解决办法

    千次阅读 2018-10-01 21:44:51
    1.C++内存泄漏是什么? 内存泄漏指的是由于疏忽或错误造成了程序未能释放掉不再使用的内存。 2.造成的后果 性能不良,内存会耗尽 3.C++没有垃圾回收机制,我们需要关注那些类型的内存泄漏? 堆内存泄漏。在内存...
  • C++内存泄漏解决方法

    万次阅读 2017-07-13 13:53:16
    1.首先说到c++内存泄漏时要知道它的含义? 内存泄漏(memory leak)是指由于疏忽或错误造成了程序未能释放掉不再使用的内存的情况。内存泄漏并非指内存在物理上的消失,而是应用程序分配某段内存后,由于设计错误,...
  •   内存泄露就是指该被GC垃圾回收的,由于有另外一个对象仍然在引用它,导致无法回收,造成内存泄露,过多的内存泄露会导致OOM。   android中的内存泄露通常是Activity或者Fragment的泄露。下文分析以Activity...
  • 一直以来java都占据着语言排行榜的头把交椅。这是与java的设计密不可分的,其中最令大家喜欢的不是面向对象,而是垃圾回收机制。你只需要简单的创建对象而不需要负责...下面我们将详细的学习什么是内存泄露,为什么
  • react出现内存泄漏的原因及解决方法

    千次阅读 2018-06-10 18:22:48
    react出现内存泄漏的原因及解决方法 今天我在写项目的时候出现了报错,经过翻译说是存在内存泄漏。 警告:不能在卸载组件上调用setState(或强迫)。这是一个无操作,但它表示应用程序中的内存泄漏。要修复,...
  • ThreadLocal内存泄漏原因以及避免方案

    千次阅读 2018-08-07 21:37:58
    ThreadLocal的原理是操作Thread内部的一个...java中的弱引用在内存不足的时候会被回收掉,回收之后变成(null,value)的形式,key被收回掉了。 如果线程执行完之后销毁,value也会被回收,这样也没问题。但如果是...
  • 如果编写的c++程序出现内存泄露了,不要慌忙,你要相信任何错误都是人为造成的,只要是人为的,你一定能找到错误所在,只不过是时间的问题而已。在面对内存泄露,如果程序不是特别长的话,你可以使用人工校验,着重...
  • 内存溢出 out of memory,...内存泄露 memory leak,是指程序在申请内存后,无法释放已申请的内存空间,一次内存泄露危害可以忽略,但内存泄露堆积后果很严重,无论多少内存,迟早会被占光。 memory leak会最终会导...
  • 一次JAVA内存泄漏的定位分析过程

    千次阅读 2018-09-21 09:46:50
    正式环境老是内存用完崩溃。 在本地模拟以及解决过程 查询JAVA线程,命令:jps -...在Leak Suspects页面会给出可能的内存泄露,进入Leak Suspects,查看那些类可能发生内存泄露 发现自己写的类: 线程池的...
  • 内存泄漏和内存溢出的区别与解决方式

    万次阅读 多人点赞 2018-08-03 15:03:07
    内存泄露 :是指程序在申请内存后,无法释放已申请的内存空间就造成了内存泄漏,一次内存泄漏似乎不会有大的影响,但内存泄漏堆积后的后果就是内存溢出。 我们知道了内存泄漏的原因而内存溢出则有可能是因为我们...
  • 具体可见
  • Toast导致Activity内存泄露解决方法

    千次阅读 2016-11-16 11:15:13
    写了一个工具类 ToastUtils,里面实现了连续点击不重复弹出的Toast private static Toast toast; ...public static void toastInBottom(Context context, String tip) { ... toast = Toast.makeText(context,
  • 先转载一篇文章:activity contex...通过这篇文章,可以理出这样一个思路来,即这Activity、Fragment对象本身的内存泄漏,其实都是来源于循环引用。比如,Activity/Fragment内部引用了对象(如:View),如果把这些对象
  • 什么情况下会导致内存泄露

    千次阅读 2016-10-12 17:28:40
    下面说明几点可能导致内存泄露的原因,供大家参考。 1.对象内存过大 保存了多个好用内存过大的对象,造成内存超出限制。 2.资源释放 程序代码的问题,长期保持某些资源,如Context,Cursor,IO流的引用,资源
  • 内存泄漏(memory leak):是指程序在申请内存后,无法释放已申请的内存空间,导致系统无法及时回收内存并且分配给其他进程使用。通常少次数的内存无法及时回收并不会到程序造成什么影响,但是如果在内存本身就比较...
  • Lua内存泄漏应对方法

    万次阅读 2015-01-14 01:07:37
    最近就遇到了一个内存泄露的问题,泄露发生在lua里,项目代码里以前的开发团队留下了检测泄露的代码,但也仅限于此。由于代码量庞大,所以想从逻辑上梳理清楚哪里的引用没干掉导致了内存泄露几乎就是大海捞针。当然...
1 2 3 4 5 ... 20
收藏数 165,060
精华内容 66,024
关键字:

内存泄漏解决