Android常见内存泄露分析及MAT与Profiler内存分析时间

目录

1.Java内存分配策略 

2.堆与栈的区别

3.Java管理内存的机制

4.Java中的内存泄露

5.Android中常见的内存泄露

6.Android中内存泄露的排查与分析

7.总结


1.Java内存分配策略 

Java程序运行时的内存分配策略有三种:静态分配、栈式分配和堆式分配。对应的存储区域如下:

  • 静态存储区(方法区):主要存放静态数据、全局static数据和常量。这块内存在程序编译时就已经分配好,并且在程序整个运行期间都存在。
  • 栈区:方法体内的局部变量都在栈上创建,并在方法执行结束时这些局部变量所持有的内存都会自动被释放。
  • 堆区:又称动态内存分配,通常就是指在程序运行时直接new出来的内存。这部分内存在不使用时将会由Java垃圾回收器来负责回收。

2.堆与栈的区别

栈内存:在方法体内定义的局部变量(一些基本类型的变量和对象的引用变量)都是在方法的栈内存中分配的。当在一段方法块中定义一个变量时,Java就会在栈中为该变量分配内存空间,当超过该变量的作用域后,分配给它的内存空间将被释放掉,该内存空间可以被重新使用。

堆内存:用来存放所有由new创建的对象(包括该对象其中的所有成员变量)和数组。在堆中分配的内存,将由Java垃圾回收器来自动管理。在堆中产生了一个数组或者对象后,还可以在栈中定义一个特殊的变量,这个变量的取值等于数组或者对象在堆内存中的首地址,这个特殊的变量就是我们上面说的引用变量。我们可以通过这个引用变量来访问堆中的对象或者数组。

例子:

public class A{
        int a=0;
        B b = new B();
        public void test(){
            int a1 = 1;
            B b1 = new B();
        }
}
A object = new A();
  • A类内的局部变量都存在栈中,包括基本的数据类型a1和引用变量b1,b1指向的B对象实体存于在堆中;
  • 引用变量object存在于栈中,而object指向的对象实体存在于堆中,包括这个对象的所有成员变量a和b,而引用变量b指向的B类型对象实体存在于堆中;

3.Java管理内存的机制

Java的内存管理就是对象的分配和释放问题。内存的分配是由程序员来完成,内存的释放由GC(垃圾回收器)完成。GC为了能够正确释放对象,必须监控每一个对象的运行状态,包括对象申请、引用、被引用、赋值等。这是Java程序运行较慢的原因之一。

释放对象的原则:该对象不再被引用。

GC的工作用原理:

将对象考虑为有向图的顶点,将引用关系考虑为有向图的有向边,有向边从引用者指向被引对象。另外,每个线程对象可以作为一个图的起始顶点,例如大多程序从main进程开始执行,那么该图就是以main进程为顶点开始的一颗根树。在有向图中,根顶点可达的对象都是有效对象,GC将不回收这些对象。如果某个对象与这个跟顶点不可达,那么我们认为这个对象不再被引用,可以被GC回收。

下面举一个例子说明如何用有向图表示内存管理。对于程序的每一个时刻,我们都有一个有向图表示JVM的内存分配情况。以下右图,就是左右程序运行到第6行的示意图。

另外:Java使用有向图的方式进行内存管理,可以消除引用循环的问题,例如有三个对象相互引用,但是只要他们和跟进程不可达,那么GC也是可以回收他们的。当然,除了有向图的方式,还有一些别的内存管理技术,不同内存管理技术各有优缺点,这里就不详细展开了。

4.Java中的内存泄露

如果一个对象满足以下两个条件:

(1)这些对象是可达的,即在有向图中,存在通路可以与其相连;

(2)这些对象是无用的,即程序以后不会再使用这些对象;

就可以判断为Java中的内存泄漏,这些对象不会被GC所回收,继续占用着内存。

在C++中,内存泄漏的范围更大一些。有些对象被分配了内存空间,然后却不可达,由于C++中没有GC,这些内存将将永远收不回来。在Java中,这些不可达的对象都有GC负责回收,因此程序员不需要考虑这部分的内存泄漏。

5.Android中常见的内存泄露

(1)单例造成的内存泄漏

public class MainActivity extends Actvity implements View.OnClickListener{
    @Override
    protected void onCreate(Bundle savedInstanceState){
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Bitmap bitmap = ImageUtils.getInstance(this).getBitmap(R.drawable.arrow);
    }
}

public class ImageUtils{
    private static ImageUtils imageUtils;
    private Context context;
    private ImageUtils(Context context){
        this.context = context;
    }
    public static ImageUtils getInstance(Context context){
        if(imageUtils == null){
            imageUtils = new ImageUtils(context);
        }
        return imageUtils;
    }    
}

这是一个普通的单例模式,当创建这个单例的时候,由于需要传入一个Context,所有这个Context的生命周期的长短至关重要:

1.如果此时传入的是Application的Context,因为Application的生命周期就是整个应用的生命周期,所以没有任何问题。

2.如果此时传入的是Activity的Context,当这个Context所对应Activity退出时,由于该Context的引用被单例对象所引用,其生命周期等于整个应用程序的生命周期,所以当前Activity退出时他的内存并不会被回收,他就造成泄露了。

当然,Application的Context不是万能的,所以不能随便乱用,例如Dialog必须使用Activity的Context。对于这部分有兴趣的读者可以自行搜索相关资料。

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

publ
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值