0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

什么是内存泄漏?android中导致内存泄漏的主要几个点

哆啦安全 来源:程序员Android 2024-02-20 10:17 次阅读

一、什么是内存泄漏?

当一个对象已经不需要在使用了,本应该被回收,而另一个正在使用的对象持有它的引用,导致对象不能被回收。因为不能被及时回收的本该被回收的内存,就产生了内存泄漏。如果内存泄漏太多会导致程序没有办法申请内存,最后出现内存溢出的错误。

二、android中导致内存泄漏的主要几个点

android开发中经常出现的点,我有只有了解了,才能更好的避免。

使用单例模式

使用匿名内部类

使用异步事件处理机制Handler

使用静态变量

资源未关闭

设置监听

使用AsyncTask

使用Bitmap

上面就是我列出的几个常出现内存泄漏的几个点,下面我们将一一解读。

三、java虚拟机内存管理

5f5b9194-cf87-11ee-a297-92fbcf53809c.jpg

java虚拟机内存分为虚拟机栈,本地方法栈,程序计数器,堆,方法区这几个模块,下面我们就来分析下各个模块。

(1).虚拟机栈

虚拟机栈主要的作用就是为执行java方法服务的,是Java方法执行的动态内存模型。会导致栈内存溢出(StackOverFlowError)

(2).本地方法栈

为执行native方法服务的,其他和虚拟机栈一样

(3).程序计数器

是当前线程执行的字节码行号指示器
处于线程独占区
如果是执行的是java代码,当前值为字节码指令的地址,如果是Native,值为undefined

(4).堆

存放对象的实例
垃圾收集器管理的主要区域
分代管理对象
会导致内存溢出(OutOfMemoryError)

(5).方法区

存放虚拟机加载的类信息,常量,静态变量,编译后的代码和数据
GC主要对方法区进行常量回收和类卸载
会出现内存溢出(OutOfMemoryError)

四、java内存几种分配策略?

可以结合上面的内存分配模型,能很好的理解。

(1).静态的

静态存储区:内存在程序编译期间就已经分配完成,一般来说,这个区域在程序运行期间一直处在
它主要储存静态数据,全局静态数据和常量

(2).栈式的

执行方法时,存储局部变量(编译期间,已经确定占用内存大小),操作数,动态链接,方法出口

(3).堆式的

也叫动态内存分配,主要存储对象实例,以及已经被加载类的Class对象(用于反射)

五、垃圾收集器是如何判断对象是否可回收?

我们知道内存泄漏的原因是应该被回收的对象,不能被及时回收,那么GC是如何来判断对象是否为垃圾对象呢?

判断的方式有两个:

引用计数
对象被引用,引用计数器加1,反之减一,只有引用计数为0,那么这个对象为垃圾对象

可达性
从GCRoot节点对象开始,看是否可以访问到此对象,如果没有访问到则为垃圾对象

可以作为GCRoot对象有以下几种:
虚拟机栈中的局部变量
本地方法栈中的引用对象
方法区中的常量引用对象
方法区中的类属性引用对象
在native层和早期的虚拟机一般使用引用计数,但是现在的java虚拟机大多使用的是可达性。

六、什么是内存抖动?

堆内存都有一定的大小,能容纳的数据是有限制的,当Java堆的大小太大时,垃圾收集会启动停止堆中不再应用的对象,来释放内存。当在极短时间内分配给对象和回收对象的过程就是内存抖动。

七、内存抖动产生的原因?

从术语上来讲就是极短时间内分配给对象和回收对象的过程。
一般多是在循环语句中创建临时对象,在绘制时配置大量对象或者执行动画时创建大量临时对象
内存抖动会带来UI的卡顿,因为大量的对象创建,会很快消耗剩余内存,导致GC回收,GC会占用大量的帧绘制时间,从而导致UI卡顿,关于UI卡顿会在后面章节讲到。

八、android中4种引用

(1).StrongReference强引用
从不被回收,java虚拟机停止时,才终止

(2).SoftReference软引用
当内存不足时,会主动回收,使用SoftReference使用结合ReferenceQueue构造有效期短

(3).WeakReference弱引用
每次垃圾回收时,被回收

(4).PhatomReference虚引用
每次垃圾回收时,被回收.结合ReferenceQueue来跟踪对象被垃圾回收器回收的活动

九、常见的导致内存泄漏的示例

(1).使用单例模式

    private static ComonUtil mInstance = null;
    private Context mContext = null;

    public ComonUtil(Context context) {
        mContext = context;
    }

    public static ComonUtil getInstance(Context context) {
        if (mInstance == null) {
            mInstance = new ComonUtil(context);
        }
        return mInstance;
    }

使用:

ComonUtil mComonUtil = ComonUtil.getInstance(this);

我们看到上面的代码就是我们平时使用的单例模式,当然这里没有考虑线程安全,请忽略。当我们传递进来的是Context,那么当前对象就会持有第一次实例化的Context,如果Context是Activity对象,那么就会产生内存泄漏。因为当前对象ComonUtil是静态的,生命周期和应用是一样的,只有应用退出才会释放,导致Activity不能及时释放,带来内存泄漏。

怎么解决呢?

常见的有两种方式,第一就是传入ApplicationContext,第二CommonUtil中取context.getApplicationContext()。

    public ComonUtil(Context context) {
        mContext = context.getApplicationContext();
    }

(2).使用非静态内部类

    /**
     * 非静态内部类
     */
    public void createNonStaticInnerClass(){
        CustomThread mCustomThread = new CustomThread();
        mCustomThread.start();
    }

    public class CustomThread extends Thread{
        @Override
        public void run() {
            super.run();
            while (true){
                try {
                    Thread.sleep(5000);
                    Log.i(TAG,"CustomThread ------- 打印");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }

我们就以线程为例,当Activity调用了createNonStaticInnerClass方法,然后退出当前Activity时,因为线程还在后台执行且当前线程持有Activity引用,只有等到线程执行完毕,Activitiy才能得到释放,导致内存泄漏。
常用的解决方法有很多,第一把线程类声明为静态的类,如果要用到Activity对象,那么就作为参数传入且为WeakReference,第二在Activity的onDestroy时,停止线程的执行。

public static class CustomThread extends Thread{
    private WeakReference mActivity;
    public CustomThread(MainActivity activity){
        mActivity = new WeakReference(activity)
    }
}

(3).使用异步事件处理机制Handler

    /**
     * 异步消息处理机制  -- handler机制
     */
    public void createHandler(){
        mHandler.sendEmptyMessage(0);
    }
    public Handler mHandler = new Handler(new Handler.Callback() {
        @Override
        public boolean handleMessage(Message msg) {
            //处理耗时操作   
            return false;
        }
    });

这个应该是我们平时使用最多的一种方式,如果当handler中处理的是耗时操作,或者当前消息队列中消息很多时,那当Activity退出时,当前message中持有handler的引用,handler又持有Activity的引用,导致Activity不能及时的释放,引起内存泄漏的问题。

解决handler引起的内存泄漏问题常用的两种方式:

1.和上面解决Thread的方式一样,

2.在onDestroy中调用mHandler.removeCallbacksAndMessages(null)

    @Override
    protected void onDestroy() {
        super.onDestroy();
        mHandler.removeCallbacksAndMessages(null);
    }

(4).使用静态变量

同单例引起的内存泄漏。

(5).资源未关闭

常见的就是数据库游标没有关闭,对象文件流没有关闭,主要记得关闭就OK了。

(6).设置监听

常见的是在观察者模式中出现,我们在退出Acviity时没有取消监听,导致被观察者还持有当前Activity的引用,从而引起内存泄漏。
常见的解决方法就是在onPause中注消监听

(7).使用AsyncTask

    public AsyncTask mTask = new AsyncTask() {

        @Override
        protected Object doInBackground(Object... params) {
            //耗时操作
            return null;
        }

        @Override
        protected void onPostExecute(Object o) {
        
        }   
    };

和上面同样的道理,匿名内部类持有外部类的引用,AsyncTask耗时操作导致Activity不能及时释放,引起内存泄漏。

解决方法同上:

1.声明为静态类,
2.在onPause中取消任务

(8).使用Bitmap

我们知道当bitmap对象没有被使用(引用),gc会回收bitmap的占用内存,当时这边的内存指的是java层的,那么本地内存的释放呢?我们可以通过调用bitmap.recycle()来释放C层上的内存,防止本地内存泄漏




审核编辑:刘清

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • Android
    +关注

    关注

    12

    文章

    3923

    浏览量

    127119
  • JAVA
    +关注

    关注

    19

    文章

    2957

    浏览量

    104537
  • 虚拟机
    +关注

    关注

    1

    文章

    908

    浏览量

    28071
  • 内存泄漏
    +关注

    关注

    0

    文章

    39

    浏览量

    9204

原文标题:Android内存泄漏知识点

文章出处:【微信号:哆啦安全,微信公众号:哆啦安全】欢迎添加关注!文章转载请注明出处。

收藏 人收藏

    评论

    相关推荐

    Linux内存泄漏检测实现原理与实现

    在使用没有垃圾回收的语言时(如 C/C++),可能由于忘记释放内存导致内存被耗尽,这叫 内存泄漏。由于内核也需要自己管理
    发表于 12-09 11:11 958次阅读

    细说Linux内存泄漏检测实现原理与实现

    在使用没有垃圾回收的语言时(如 C/C++),可能由于忘记释放内存导致内存被耗尽,这叫 内存泄漏。由于内核也需要自己管理
    发表于 07-03 09:22 465次阅读
    细说Linux<b class='flag-5'>内存</b><b class='flag-5'>泄漏</b>检测实现原理与实现

    内存泄漏定位该如何去实现呢

    嵌入式之内存泄漏定位篇在嵌入式开发中,经常会使用malloc,free分配释放堆内存,当malloc,free不配对使用时,就会导致内存
    发表于 12-17 07:24

    分享一种内存泄漏定位排查技巧

    常见的泄漏方式在嵌入式开发中,经常会使用malloc,free分配释放堆内存,稍不小心就可能导致内存点点地泄露,直至堆
    发表于 12-17 08:13

    内存泄漏的特点和类型

    在计算机科学中,内存泄漏(memory leak)指由于疏忽或错误使程序未能释放而造成不能再使用的内存的情况。内存泄漏并非指
    的头像 发表于 06-20 10:58 2788次阅读

    内存泄漏问题原理及检视方法

    可能不少开发者都遇到过内存泄漏导致的网上问题,具体表现为单板在现网运行数月以后,因为内存耗尽而导致单板复位现象。一方面,
    的头像 发表于 10-10 10:42 2507次阅读

    什么是内存泄漏内存泄漏有哪些现象

    内存泄漏几乎是很难避免的,不管是老手还是新手,都存在这个问题,甚至 Windows 与 Linux 这类系统软件也或多或少存在着内存泄漏
    的头像 发表于 09-05 17:24 9604次阅读

    Linux内存泄漏检测实现原理与实现

    在使用没有垃圾回收的语言时(如 C/C++),可能由于忘记释放内存导致内存被耗尽,这叫 内存泄漏
    的头像 发表于 07-03 09:21 606次阅读
    Linux<b class='flag-5'>内存</b><b class='flag-5'>泄漏</b>检测实现原理与实现

    什么是内存泄漏?如何避免JavaScript内存泄漏

    JavaScript 代码中常见的内存泄漏的常见来源: 研究内存泄漏问题就相当于寻找符合垃圾回收机制的编程方式,有效避免对象引用的问题。
    发表于 10-27 11:30 360次阅读
    什么是<b class='flag-5'>内存</b><b class='flag-5'>泄漏</b>?如何避免JavaScript<b class='flag-5'>内存</b><b class='flag-5'>泄漏</b>

    内存泄漏如何避免

    的数,那就是内存溢出。 2. 内存泄漏 内存泄露 memory leak,是指程序在申请内存后,无法释放已申请的
    的头像 发表于 11-10 11:04 705次阅读
    <b class='flag-5'>内存</b><b class='flag-5'>泄漏</b>如何避免

    线程内存泄漏问题的定位

    记录一个关于线程内存泄漏问题的定位过程,以及过程中的收获。 1. 初步定位 是否存在内存泄漏:想到内存
    的头像 发表于 11-13 11:38 580次阅读
    线程<b class='flag-5'>内存</b><b class='flag-5'>泄漏</b>问题的定位

    如何发现内存泄漏

    由于 C 和 C++ 程序中完全由程序员自主申请和释放内存,稍不注意,就会在系统中导入内存错误。同时,内存错误往往非常严重,一般会带来诸如系统崩溃,内存耗尽这样严重的 后果。本文将从静
    的头像 发表于 11-13 15:41 567次阅读

    内存溢出与内存泄漏:定义、区别与解决方案

    与区别 1. 定义: 内存溢出(Memory Overflow)指的是程序在申请内存时,无法获得足够的内存空间,导致程序抛出异常或崩溃。当程序需要的
    的头像 发表于 12-19 14:10 2469次阅读

    C语言内存泄漏问题原理

    内存泄漏问题只有在使用堆内存的时候才会出现,栈内存不存在内存泄漏问题,因为栈
    发表于 03-19 11:38 478次阅读
    C语言<b class='flag-5'>内存</b><b class='flag-5'>泄漏</b>问题原理

    如何检测内存泄漏

    检测内存泄漏是软件开发过程中一项至关重要的任务,它有助于识别和解决那些导致程序占用过多内存资源,从而影响程序性能甚至导致程序崩溃的问题。以下
    的头像 发表于 07-30 11:50 1545次阅读