Android 性能优化系列(一):内存优化

分配内存的时候发生了什么

当你new 一个对象的时候,到底发生了什么

  • 首先会为你的对象分配内存,如果成功就成功,如果失败就进行下一步
  • 下一步:进行GC,注意这次GC不包括软引用,然后分配内存,如果成功就成功了,如果失败,就进行下一步
  • 下一步:增大Heap堆内存,每个应用都是有一个最大的堆内存空间,但是不是一开始就给你最大,是慢慢给的,然后分配内存,如果成功就成功,如果失败,就进行下一步
  • 下一步:进行GC,注意这次GC带有软引用,然后在分配内存,如果成功就成功了,如果失败,就直接oom
  • 源码在vm/alloc/Head.cpp

指导思想

  • 内存尽量少用
  • 内存尽量复用

Bitmap的内存优化

参考我之前写的一篇博客
Bitmap的优化

避免不必要的对象创建

尽量少用

避免进行自动装箱

自动装箱会把一个基本数据类型改为一个引用数据类型,这种虽然方便了我们的使用,但是某些情况下使用不当会导致性能问题

第一点:使用操作符时候的自动装箱

  public static void badAssemble() {
        Integer sum = 0;
        for (int i = 0; i < (1 << 30); i++) {
            sum = sum + i;
        }
    }

这种就会创建很多的临时对象Integer,他们又会被GC回收,就会出现内存抖动的情况

第二点:使用容器时的自动装箱

比如我们使用HashMap的时候,传入的key,这些key值就会自动装箱成对象,此时我们可以选择用,android平台提供的优化容器,来避免自动装箱的操作

这些容器包括SparseArraySparseBooleanArraySparseIntArraySparseLongArray

这些容器的优点

  • 当key值为int基本数据类型时,避免了自动装箱的操作,同时这也是他的局限性
  • 其内部使用俩个数储存数据的,一个用于储存key,一个用于储存value

使用注解代替枚举

枚举比常量大3倍左右,所以android平台提供了注解方式检查类型安全,目前提供了int和String类型,IntDef和StringDef,用来提供编译期的类型检查。

使用这个俩个注解需要引入依赖

compile 'com.android.support:support-annotations:22.0.0'
public class Constant {

    public static final int FLAG_START = 0;
    public static final int FLAG_STOP = 1;
    public static final int FLAG_PAUSE = 2;

    @IntDef({FLAG_START, FLAG_STOP, FLAG_PAUSE})
    public @interface VideoState {}
}

当我们定义形参的时候,在参数之前加上注解

  public static void accept(@Constant.VideoState int videoState) {
        Log.d("OptUtils", "state=" + videoState);
    }

这个会触发编译器的静态代码检查,当传入的不是这个数据的时候,就会报错

尽量复用

线程的复用

线程用线程池管理,避免频繁的创建新的线程

合理使用对象池

当需要大量创建临时对象的时候,会导致频繁GC,导致内存抖动,使用对象池避免

避免在onDraw中创建对象

会造成内存抖动,同上

保证不使用的对象释放(防止内存泄露)

内存泄露的定义

当前不使用的对象,被GCRoot引用,导致不能释放,使可使用的内存变小

(1) 资源对象未关闭导致的内存泄露

当资源对象不再使用,用该调用close来关闭函数,然后再置为null

(2)注册的对象未注销

比如注册的广播,应该及时注销

(3) 类的静态变量持有大量对象

类的静态变量,属于长声明周期,持有段声明周期的对象,对导致短生命周期对象不能释放,造成内存泄露

比如单例持有Activity的Context,这种解决方法是传入Aplication的Context

(4)非静态内部类的静态实例

非静态内部类会持有外部类的引用,如果非静态内部类创建了静态实例,那么他就变成了长生命周期,导致外部类的对象不能释放

这种的解决方法就是,将内部类抽取成一个单例使用

(5)Handler内存泄露

message发出后会储存到MessageQueue中,Message中的target,他是Handler的一个引用,如果Message在MessageQueue储存时间太长,就会导致Handler不能释放,如果Handler是非静态内部类,那么他会持有外部类对象,导致外部类不能释放,这种情况发生在Handler发送了10s的延迟消息,但是外部类Activity5s就关闭了,导致内存泄露

解决方案:
(1)把Handler置为静态内部类,需要传入的Context引用,用软引用修饰,保证他可以正常释放
(2)在Activity销毁的时候,在onDestory移除Handler的消息队列里的消息

(6)WebView的内存泄露

WebView都存在内存泄露问题,应用只要使用一次WebView,内存就不会被释放

解决方法:为WebView开启一个进程,用AIDL进行通信,WebView的进程可以根据需求,在需要的时候进行销毁

查看内存泄露和性能优化的工具

推荐看下面几篇博客

Android Studio 3.0 Memory Profiler使用:

内存泄露检测

内存泄露检测

https://juejin.im/post/5b7cbf6f518825430810bcc6#heading-7

LeakCanary 工具,很方便的找内存泄露

LeakCanary

Lint检测代码

Lint检测代码

发布了100 篇原创文章 · 获赞 5 · 访问量 2万+

猜你喜欢

转载自blog.csdn.net/qq_34760508/article/details/99732223
今日推荐