分配内存的时候发生了什么
当你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平台提供的优化容器,来避免自动装箱的操作
这些容器包括SparseArray
、SparseBooleanArray
、SparseIntArray
、SparseLongArray
这些容器的优点
- 当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 工具,很方便的找内存泄露
Lint检测代码