内存泄漏_内存泄漏定位 - CSDN
内存泄漏 订阅
内存泄漏(Memory Leak)是指程序中己动态分配的堆内存由于某种原因程序未释放或无法释放,造成系统内存的浪费,导致程序运行速度减慢甚至系统崩溃等严重后果。 展开全文
内存泄漏(Memory Leak)是指程序中己动态分配的堆内存由于某种原因程序未释放或无法释放,造成系统内存的浪费,导致程序运行速度减慢甚至系统崩溃等严重后果。
信息
外文名
Memory Leak
中文名
内存泄漏
内存泄漏简介
内存泄漏(Memory Leak)是指程序中己动态分配的堆内存由于某种原因程序未释放或无法释放,造成系统内存的浪费,导致程序运行速度减慢甚至系统崩溃等严重后果。内存泄漏缺陷具有隐蔽性、积累性的特征,比其他内存非法访问错误更难检测。因为内存泄漏的产生原因是内存块未被释放,属于遗漏型缺陷而不是过错型缺陷。此外,内存泄漏通常不会直接产生可观察的错误症状,而是逐渐积累,降低系统整体性能,极端的情况下可能使系统崩溃。随着计算机应用需求的日益增加,应用程序的设计与开发也相应的日趋复杂,开发人员在程序实现的过程中处理的变量也大量增加,如何有效进行内存分配和释放,防止内存泄漏的问题变得越来越突出。例如服务器应用软件,需要长时间的运行,不断的处理由客户端发来的请求,如果没有有效的内存管理,每处理一次请求信息就有一定的内存泄漏。这样不仅影响到服务器的性能,还可能造成整个系统的崩溃。因此,内存管理成为软件设计开发人员在设计中考虑的主要方面 [1]  。
收起全文
精华内容
参与话题
  • 1.什么是内存泄漏(Memory Leak)? 简单地说就是申请了一块内存空间,使用完毕后没有释放掉。它的一般表现方式是程序运行时间越长,占用内存越多,最终用尽全部内存,整个系统崩溃。由程序申请的一块内存,且没有任何...

    1.什么是内存泄漏(Memory Leak)? 

       简单地说就是申请了一块内存空间,使用完毕后没有释放掉。它的一般表现方式是程序运行时间越长,占用内存越多,最终用尽全部内存,整个系统崩溃。由程序申请的一块内存,且没有任何一个指针指向它,那么这块内存就泄露了。

    2、如何检测内存泄露

    第一:良好的编码习惯,尽量在涉及内存的程序段,检测出内存泄露。当程式稳定之后,在来检测内存泄露时,无疑增加了排除的困难和复杂度。使用了内存分配的函数,一旦使用完毕,要记得要使用其相应的函数释放掉。

    第二:将分配的内存的指针以链表的形式自行管理,使用完毕之后从链表中删除,程序结束时可检查改链表。

    第三:Boost 中的smart pointer。

    第四:一些常见的工具插件,如ccmalloc、Dmalloc、Leaky等等。


    展开全文
  • 具体可见

    一:什么是内存泄露
    内存泄露是指:内存泄漏也称作"存储渗漏",用动态存储分配函数动态开辟的空间,在使用完毕后未释放,结果导致一直占据该内存单元。直到程序结束。(其实说白了就是该内存空间使用完毕之后未回收)即所谓内存泄漏。

    二:常见的内存泄露造成的原因
    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;
        }
    }
    

    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 {
        //...
        }
    }   
    

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

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

    万次阅读 2018-06-08 16:14:24
    (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内存泄漏总结

    展开全文
  • 内存泄漏和内存溢出

    万次阅读 2018-08-22 15:40:35
    内存泄漏:(Memory Leak)是指程序中己动态分配的堆内存由于某种原因程序未释放或无法释放,造成系统内存的浪费,导致程序运行速度减慢甚至系统崩溃等严重后果   内存泄露 memory leak,是指程序在申请内存后,...

    内存溢出:(out of memory)通俗理解就是内存不够,通常在运行大型软件或游戏时,软件或游戏所需要的内存远远超出了你主机内安装的内存所承受大小,就叫内存溢出。

    内存泄漏:(Memory Leak)是指程序中己动态分配的堆内存由于某种原因程序未释放或无法释放,造成系统内存的浪费,导致程序运行速度减慢甚至系统崩溃等严重后果

     

    内存泄露 memory leak,是指程序在申请内存后,无法释放已申请的内存空间,一次内存泄露危害可以忽略,但内存泄露堆积后果很严重,无论多少内存,迟早会被占光。导致内存溢出   内存溢出 out of memory,是指程序在申请内存时,没有足够的内存空间供其使用,出现out of memory;比如申请了一个integer,但给它存了long才能存下的数,那就是内存溢出。

     

    通过Xcode内置的APP性能调试工具Instruments和开源的内存泄漏检测工具MLeaksFinder(https://github.com/Zepo/MLeaksFinder)进行内存泄露问题定位,修复泄露问题后,分析内存使用数据变化。

     

    前言

    最近在项目中偶尔会发现内存泄漏现象。一开始还是一脸懵逼的查来查去:这怎么就泄漏了?这样竟然没泄漏?一直没有个清晰地思路。这几天闲下来,打算认真整理学习一下。我在这里从一个“如何主动造成内存泄漏”的角度来学习,然后熟悉一下不同方法检测的结果如何,这样以后再遇到相关问题时就能够很快的解决了。

    java gc

    首先要有一个大前提,也就是java gc。在大部分虚拟机(包括Android的ART)中,Java都采用了“可达性分析”算法来进行内存回收,原理是:会有几个引用作为root节点,对于任意对象来说,如果从root层层遍历,如果找不到对于他的引用链,那么这个对象就被标记为无用,就会在gc时被销毁。

    为何泄漏?

    内存泄漏,即部分对象虽然已经不再使用,但是因为有root持有引用,所以并没有被销毁,所占用的内存一直没有被释放。一次两次发生影响不大。如果频繁发生,那么可用内存会渐渐不足,最终在某一次请求内存时发现内存不足而发生oom。这里要明确一个概念,只有强引用会发生内存泄漏,而weak等引用因为其特殊机制,所以影响不大。

    泄露影响比较大的就是一些大对象,常见的比如某些资源,bitmap,以及activity。

    如何发生泄漏

    首先让我们从另一个角度来看,如何主动发生内存泄漏呢?当然是想办法给他一个一直存在的强引用了。

     

     引起内存溢出的原因有很多种,小编列举一下常见的有以下几种: 1.内存中加载的数据量过于庞大,如一次从数据库取出过多数据; 2.集合类中有对对象的引用,使用完后未清空,使得JVM不能回收; 3.代码中存在死循环或循环产生过多重复的对象实体; 4.使用的

    第三方软件

    中的BUG; 5.启动参数内存值设定的过小 内存溢出的解决方案: 第一步,修改JVM启动参数,直接增加内存。(-Xms,-Xmx参数一定不要忘记加。) 第二步,检查错误日志,查看“OutOfMemory”错误前是否有其它异常或错误。 第三步,对代码进行走查和分析,找出可能发生内存溢出的位置。 重点排查以下几点: 1.检查对数据库查询中,是否有一次获得全部数据的查询。一般来说,如果一次取十万条记录到内存,就可能引起内 存溢出。这个问题比较隐蔽,在上线前,数据库中数据较少,不容易出问题,上线后,数据库中数据多了,一次查 询就有可能引起内存溢出。因此对于数据库查询尽量采用分页的方式查询。 2.检查代码中是否有死循环或递归调用。 3.检查是否有大循环重复产生新对象实体。5.检查List、MAP等集合对象是否有使用完后,未清除的问题。List、MAP等集合对象会始终存有对对象的引用,使 得这些对象不能被GC回收。 第四步,使用内存查看工具动态查看内存使用情况

     

    static

    static这个关键字使一个变量变为只和这个类相关的类变量,和实例无关。他的生命周期是很长的贯穿于app的启动到关闭。因此只要用一个static引用一个大对象,就可以泄漏了!举个例子:

    static Activity activity;

    这是最简单粗暴的持有一个activity的引用,这样这个activity退出之后对象并没有被销毁。

    static View view;

    一个View初始化时会用到context,我们在自定义View,重写构造方法时就知道这个了。因此如果一个View也像这样被持有,那个context也不会被释放。

    innerClass

    内部类有个特性,是他会持有一个外部类的引用。如果内部类的实例一直存活,那么外部类activity的实例也就一直在。比如持有一个static的内部类引用:

    或者以前我们用asynctask时喜欢搞一个匿名内部类执行异步任务,那当我们activity退出后这个异步任务还在执行的话,就会泄露了。

    还有自己开个匿名线程:

    还有在使用handler时,如果用了匿名handler,那么这个handler会带着activity的引用藏到消息队列中。消息没有被处理,就会造成内存泄漏。类似的,还有timertask等。

    register

    我们平时会用到很多第三方库,比如ButterKnife EventBus RxJava等等,有的时候要获取系统服务,getSystemService。在使用的时候,都有一个先registerd或者bind的操作,而且在创建的时候会把activity的引用传过去。如果在activity结束时没有unregister或者unbind,就会造成内存泄漏。

    如何检测泄漏

    最简单的方法自然就是使用leakcanary了。只要给自己的项目加上这个工具,在发生泄漏的时候很快就会有提示。

    除此之外,android studio的刀耕火种的方式也不错,在这里我拿一个例子来示范一下我是怎么用的。

    准备工作

    首先,我写了两个activity,一个MainActivity,一个MemoryLeakActivity,逻辑是:MainActivity中有个按钮,点击会调到MemoryLeakActivity,在这个activity中会故意发生内存泄漏,代码如下:

    在开始之前,再熟悉一下这个

    这个Monitors可以观察当前选中app的运行状态,现在只需要关注我标了123的地方。

    首先这个Memory就是当前app的内存使用状况:

    1. 产生一个当前java堆的.hprof文件,这个文件反映了当前时刻java堆中内存详情,记住这个玩意有大用!

    2. 手动进行一次gc

    3. 这一块很重要,首先他有两个部分,蓝色和灰色。蓝色部分是当前内存使用大小,灰色部分是这个app被限制的最大内存大小。当蓝色部分越来越大,最后和灰色部分一样时,说明我们内存使用很多了即将内存不足,此时会进行一次gc同时将回灰色部分即限制的大小提高。

    肉眼观察

    好了,介绍完这个工具,我们开始动手实践。首先打开app,点击按钮跳到会发生泄漏的activity上,再按返回键,然后再次按下按钮……这样反复操作:

    与此同时,观察monitors的memory窗口,会发现蓝色部分在每一次开启新activity时会增长一部分,这很正常。但是在返回时,明明activity被“退出”了,但是蓝色部分还是没有变化。反复几次之后,蓝色部分一直在增长。也就是说当前内存越用越多,可以推断已经发生内存泄漏啦~

    自动分析

    接下来由android studio来分析一下。在反复几次上面的操作之后,返回MainActivity,然后点击dump java heap按钮,然后等一会儿,android studio在为我们dump此时的horof文件。在成功后,会自动打开:

    如图在这个界面中,我们看最右面有一个栏叫 Analyzer Tasks,打开它,会发现有两个选项。我们是来看activity的内存泄漏的,那就把那个查重复字符串的√去掉。然后点右边那个绿色小三角,会发现下面Analysis Results栏里面展示出了当前泄露的Activity引用:

    点击第一个item,最下方Reference Tree栏中便展示出了具体的引用:

    一般来说,第一个就是我们发生泄漏的地方。在图中,this$0的意思是隐式的引用。也就是说,我们的activity是因为一个内部类而发生了内存泄漏。

    可以看到显式的有一个leakCntextRef引用,这说明我们有一个名为leakCntextRef的引用持有了activity。回过头看看我们的代码,果然,验证的没错。

    拓展

    android studio的分析还算比较简单而且内容较少,我们可以把这个hprof导出,然后用mat来分析。

    怎么解决泄漏

    既然发生了泄漏,那就要解决它,避免问题出现。那么怎么解决呢?很简单,泄漏是因为持有了activity引用导致无法被销毁,那么只有两个选择:及时取消引用,或者让这个引用多待一会,但是该gc的时候就销毁。

    根据这个思路:

    • 我们在代码中能不用static变量持有contxt就不用,非要用就用weak引用。

    • 对于内部类,尽量用静态内部类,这样就不会持有外部类引用。如果需要外部类引用做一些事,就手动赋给一个weak引用。

    • 对于匿名内部类,不要图简单方便,实在不行就乖乖的写成外部类。

    • 异步操作,尽量用可以方便管理的,比如rxJava,而不是用老古董AsyncTask了。非要用也最好加一个终止条件,在退出Activity时就该结束了。

    • 在用rx时,可以在subscribe()的时候获取到Subscripeion,在不用的时候手动unSubscribe(),或者直接bind()到Activity的生命周期上,比如使用RxActivity管理。

    • 在使用handler时,记得在activity的onDestroy()中加上remove()

    • 在获取到某些资源时,使用完记得释放

    • 在用到一些大对象比如Bitmap啊什么的,要记得回收

    • 最后,在使用各种第三方库或者系统服务的时候还要记得有注册或绑定就要有解除注册、解绑定。

     

    展开全文
  • 野指针 避免野指针 良好的编码规范
  • 内存泄漏&amp;悬挂指针(野指针)的危害及避免 什么是内存泄漏内存泄漏:动态申请的内存空间没有正常释放,但是也不能正确使用的情况。 内存泄漏的危害? 一般用户感觉不到内存泄漏的危害,真正有危害的...
  • 野指针出现以及如何避免问题

    千次阅读 2016-11-02 19:40:39
    指向已释放掉内存空间:比如子函数返回值为局部变量的指针,在子函数完成时指针指向内存已经释放,随时有可能被其他进程占用,导致某些错误。 解决方法: 1、指向不明确: 声明时记得初始化就可以了,暂时不用就指向...
  • java中内存泄露8种情况的总结

    万次阅读 多人点赞 2019-04-18 20:42:41
    由于java的JVM引入了垃圾...那么对于这种情况下,由于代码的实现不同就会出现很多种内存泄漏问题(让JVM误以为此对象还在引用中,无法回收,造成内存泄漏)。 1、静态集合类,如HashMap、LinkedList等等。如果这些...
  • 内存溢出和内存泄漏的区别

    万次阅读 多人点赞 2011-07-19 16:47:25
    内存溢出,是指程序在申请内存时,没有足够的内存空间供其使用,出现out of memory;...内存泄露,是指程序在申请内存后,无法释放已申请的内存空间,一次内存泄露危害可以忽略,但内存泄露堆积后果很严重,
  • 内存泄漏(memory leak):是指程序在申请内存后,无法释放已申请的内存空间,导致系统无法及时回收内存并且分配给其他进程使用。通常少次数的内存无法及时回收并不会到程序造成什么影响,但是如果在内存本身就比较...
  • 如何定位内存泄漏

    千次阅读 2018-07-15 22:24:00
    (1)常发性内存泄漏,发生内存泄漏的代码会被多次执行到,每次执行都会导致一块内存泄漏(2)偶发性内存泄漏(3)一次性内存泄漏,发送泄漏的代码只会被执行一次(4)隐式内存泄漏,程序在运...
  • C++内存泄漏及解决办法

    千次阅读 2018-10-01 21:44:51
    1.C++内存泄漏是什么? 内存泄漏指的是由于疏忽或错误造成了程序未能释放掉不再使用的内存。 2.造成的后果 性能不良,内存会耗尽 3.C++没有垃圾回收机制,我们需要关注那些类型的内存泄漏? 堆内存泄漏。在内存...
  • 先转载一篇文章:activity contex...通过这篇文章,可以理出这样一个思路来,即这Activity、Fragment对象本身的内存泄漏,其实都是来源于循环引用。比如,Activity/Fragment内部引用了对象(如:View),如果把这些对象
  • 闭包造成内存泄漏问题的解决办法

    千次阅读 2018-09-07 09:28:57
    由于IE的js对象和DOM对象使用不同的垃圾收集方法,因此闭包在IE中会导致内存泄露问题,也就是无法销毁驻留在内存中的元素 function closure(){ var oDiv = document.getElementById('oDiv');//oDiv用完之后一直...
  • 一直以来java都占据着语言排行榜的头把交椅。这是与java的设计密不可分的,其中最令大家喜欢的不是面向对象,而是垃圾回收机制。你只需要简单的创建对象而不需要负责...下面我们将详细的学习什么是内存泄露,为什么
  • 内存泄漏及其检测方法

    万次阅读 2019-03-07 17:39:34
    1、内存泄漏的定义   内存泄漏指的是在程序里动态申请的内存在使用完后,没有进行释放,导致这部分内存没有被系统回收,久而久之,可能导致程序内存不断增大,系统内存不足……引发一系列灾难性后果。 2、...
  • 内存泄露调试

    万次阅读 2015-08-01 17:33:08
    发生内存泄漏的代码会被多次执行到,每次被执行的时候都会导致一块内存泄漏。 2.偶发性内存泄漏。发生内存泄漏的代码只有在某些特定环境或操作过程下才会发生。 常发性和偶发性是相对的。对于特定的环境,偶发性...
  • 如果编写的c++程序出现内存泄露了,不要慌忙,你要相信任何错误都是人为造成的,只要是人为的,你一定能找到错误所在,只不过是时间的问题而已。在面对内存泄露,如果程序不是特别长的话,你可以使用人工校验,着重...
  • 什么情况下会导致内存泄露

    千次阅读 2016-10-12 17:28:40
    下面说明几点可能导致内存泄露的原因,供大家参考。 1.对象内存过大 保存了多个好用内存过大的对象,造成内存超出限制。 2.资源释放 程序代码的问题,长期保持某些资源,如Context,Cursor,IO流的引用,资源
  • 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,
1 2 3 4 5 ... 20
收藏数 306,163
精华内容 122,465
关键字:

内存泄漏