Android内存详解

一.什么是内存泄漏

通俗来说,定义了的变量没使用,就是内存泄漏了。Android虚拟机的垃圾回收采用的是根搜索算法,还一种是程序计数器算法。GC会从根节点(GC Roots)开始对heap进行遍历。到最后,部分没有直接或者间接引用到GC Roots的就是需要回收的垃圾,会被GC回收掉。而内存泄漏出现的原因就是存在了无效的引用,导致本来需要被GC的对象没有被回收掉。

详情:Java内存分配&垃圾回收详解_jianning-wu的博客-CSDN博客

二.常见的内存泄露问题举例

1.单例导致内存泄露

代码

package com.example.myapplication;

import android.content.Context;

/**
 * 不正确的单例模式导致内存泄露举例
 * */

public class SingletonTest {

    private static SingletonTest singletonTest;
    private Context context;

    private SingletonTest(Context context){
        this.context=context;
    }

    public static SingletonTest getInstance(Context context){
        if(null==singletonTest){
            singletonTest=new SingletonTest(context);
        }
        return singletonTest;
    }

}

分析

<1> 单例的含义是,其生命周期和Application一样。所以使用单例要格外小心,避免造成内存泄露。上述代码就很容易造成内存泄露。

<2> 上述代码中new SingletonTest对象的时候入参了一个Context对象,因为生命周期的问题,所以Context对象直到application销毁,Context对象才会被释放,如果该Context对象是一个Activity,就会导致该Activity该销毁的时候不能销毁导致内存泄露问题。

<3> 使用单例时,应该注意以下几点

(1) 尽量使用内部静态类初始化,这样既可以考虑到内存问题,也可以考虑到线程安全问题。

详见:常用的设计模式_android mvp mvvm_jianning-wu的博客-CSDN博客

(2) 使用单例时,入参Context或者Activity或者Fragment等等时,最好在具体方法是入参,而不是在构造方法中入参。这样避免入参的对象不能及时销毁。

(3) 使用单例,尽量避免单例持有大量数据。

2.匿名内部类导致内存泄漏

java代码

/**
 * 匿名内部类导致内存泄露举例
 * */

public class Test112233 {

    public static void main(String[] args) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                int i = 9;
                int j = 10;
                int sum = i + j;
                LoggerUtils.log("9+10=" + sum);
            }
        }).start();
    }

}

字节码

Compiled from "Test112233.java"
public class XXX.XXX.com.XXX.main.Test112233 {
  public XXX.XXX.com.XXX.main.Test112233();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: return

  public static void main(java.lang.String[]);
    Code:
       0: new           #2                  // class java/lang/Thread
       3: dup
       4: new           #3                  // class XXX/XXX/com/XXX/main/Test112233$1
       7: dup
       8: invokespecial #4                  // Method XXX/XXX/com/XXX/main/Test112233$1."<init>":()V
      11: invokespecial #5                  // Method java/lang/Thread."<init>":(Ljava/lang/Runnable;)V
      14: invokevirtual #6                  // Method java/lang/Thread.start:()V
      17: return
}

分析,main方法中

<1> code 4:new 一个匿名内部类的实例。

<2> code8:调用匿名内部类的构造方法。

这样一来生成的内部类就会持有外部类的引用,如果内部类异常,比如处理延迟的消息,不能及时销毁,就会导致外部类不能回收,将导致内存泄漏。

详见:Java查看字节码&内存分配&垃圾回收详解_jianning-wu的博客-CSDN博客

 

改成Lambda后的java代码

/**
 * 匿名内部类导致内存泄露举例
 * */

public class Test112233 {

    public static void main(String[] args) {
        new Thread(() -> {
            int i = 9;
            int j = 10;
            int sum = i + j;
            LoggerUtils.log("9+10=" + sum);
        }).start();
    }

}

改成Lambda后的字节码

Compiled from "Test112233.java"
public class XXX.XXX.com.XXX.main.Test112233 {
  public XXX.XXX.com.XXX.main.Test112233();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: return

  public static void main(java.lang.String[]);
    Code:
       0: new           #2                  // class java/lang/Thread
       3: dup
       4: invokedynamic #3,  0              // InvokeDynamic #0:run:()Ljava/lang/Runnable;
       9: invokespecial #4                  // Method java/lang/Thread."<init>":(Ljava/lang/Runnable;)V
      12: invokevirtual #5                  // Method java/lang/Thread.start:()V
      15: return
}

可见,在Lambda格式中,没有生成内部类,而是直接使用invokedynamic 指令动态调用run方法,从而避免持有外部类的引用,也就避免了内存泄漏的发生。

3.Handler导致内存泄漏

详见:Handler详解(下)_jianning-wu的博客-CSDN博客

4.Bitmap导致内存泄漏

详见:Bitmap详解(上)_canvas: trying to use a recycled bitmap_jianning-wu的博客-CSDN博客

Bitmap详解(下)_jianning-wu的博客-CSDN博客

5.文件流等资源未关闭导致内存泄漏

public static Bitmap getBitmapByFile(File file) {
    if (null == file) return null;
    InputStream is = null;
    try {
        is = new BufferedInputStream(new FileInputStream(file));
        return BitmapFactory.decodeStream(is);
    } catch (FileNotFoundException e) {
        e.printStackTrace();
        return null;
    } finally {
        FileUtils.closeIO(is);
    }
}

6.WebView导致内存泄漏

/**
 * onDestroy方法
 */

@Override
protected void onDestroy() {
    super.onDestroy();
    if (null != mWebView) {
        mWebView.destroy();
        mWebView = null;
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值