内存优化
2019-11-19 本文已影响0人
放肆滴微笑
内存优化,主要来处理:内存泄露,内存抖动
Java虚拟机
运行时数据区域分为
- 共享数据区
1.方法区- ClassLoader加载类信息,常量、静态变量、类信息,编译后的代码
- 运行时常量池,字面量public static final java常量,符号引用,类,接口全名,方法名
- java堆
- 虚拟机能管理的最大一块内存,gc主战场
- 对象实例,数组的内容
- 线程私有区
-
程序计数器,相当于一个执行代码的指示器,用来确认下一行代码的地址,每个线程都会有一个。也就是一个变量,存储下一行代码的内存地址,如下绿色
image.png
- 虚拟机栈,java虚拟机规范中定义了outofmemory和stackoverflow异常
- 本地方法栈,java虚拟机规范中定义了outofmemory和stackoverflow异常
-


内存泄露
产生原因
一个长生命周期的对象持有一个短生命周期对象的引用,通俗讲就是该回收的对象,因为引用问题没有被回收,最终OOM
Android Profiler的使用
-
Run菜单下的profile XXX 或者
image.png
- 在图型用户界面上选择要分析的一段内存
- Allocations: 动态分配对象个数
- Deallocation:解除分配的对象个数
- Total count :对象的总数
- Shallow Size:对象本身占用的内存大小
-
Retained Size:GC回收能收走的内存大小
下载出来
image.png
Mat工具使用
Mat下载地址
- 根据上面下载的文件,转换profile文件格式,打开sdk/platform-tools/hprof-conv文件,创建一个临时文件夹,cmd中找到,执行转换命令 hprof-conv -z 源文件 目标文件
- 打开文件 File-->Open Heap Dump...
-
选择Histogram
image.png
- 输入查找的类名,就可以进行分析了
image.png
解决方案
- 如果是自己写的,则进行处理
- 第三方写的,反射找到问题,处理
内存抖动
内存的频繁分配与回收,如果分配速度大于回收速度,最后也会OOM
标记清除算法Mark-Sweep

复制算法Copying

标记压缩算法Mark-Compact

强软弱虚引用
- 强引用 不回收
- 软引用 内存不足时回收,存放一些重要性不是很强又不能随便让清除的对象,比如图片切换到后台不需要马上显示了
- 弱引用 第一次扫到了,就标记下来,第二次扫到直接回收
- 虚引用 幽灵 幻影引用 不对生存造成任何影响,用于跟踪GC的回收通知
区别
虚引用只要gc扫过去,就会回收,是拿不到数据,只能从队列中获取
弱引用,可以拿到数据,gc扫过后拿不到数据,只能从队列中获取
# 虚引用
ReferenceQueue<Object> referenceQueue = new ReferenceQueue<>();
Object obj = new Object();
//虚引用
PhantomReference phantomReference = new PhantomReference(obj,referenceQueue);
//拿到虚引用的对象
System.out.println("phantomReference obj:"+phantomReference.get());
//获取一个对象
System.out.println("referenceQueue poll"+referenceQueue.poll());
obj = null;
System.gc();
Thread.sleep(2000);
//拿到虚引用的对象
System.out.println("phantomReference obj:"+phantomReference.get());
//获取一个对象
System.out.println("referenceQueue poll"+referenceQueue.poll());
通过打印发现,虚引用在放入引用中,就不能获取到对象,引用队列开始也不能获取,通过gc后,则引用无法获取对象,引用队列则记录了对象
phantomReference obj:null
referenceQueue pollnull
phantomReference obj:null
referenceQueue polljava.lang.ref.PhantomReference@32a1bec0
# 弱引用
ReferenceQueue<Object> referenceQueue = new ReferenceQueue<>();
Object obj = new Object();
WeakReference weakReference = new WeakReference(obj,referenceQueue);
//拿到弱引用的对象
System.out.println("weakReference obj:"+weakReference.get());
//获取一个对象
System.out.println("referenceQueue poll"+referenceQueue.poll());
obj = null;
System.gc();
Thread.sleep(2000);
//拿到弱引用的对象
System.out.println("weakReference obj:"+weakReference.get());
//获取一个对象
System.out.println("referenceQueue poll"+referenceQueue.poll());
通过打印 发现,弱引用在gc回收前,可以获得引用对象,引用队列中没有对象,gc后,不能获得引用对象,引用队列中可以获得之前的引用对象
weakReference obj:java.lang.Object@32a1bec0
referenceQueue pollnull
weakReference obj:null
referenceQueue polljava.lang.ref.WeakReference@22927a81
内存优化解决方案,编码习惯
- 数据类型,不要使用必须求更占空间的基本类型,能用int则不用long,float比int站空间
- 循环尽量用foreach,少用iterator,自动装箱少用
- 数据量千级别以内,可以使用Sparse数组,(key为整数),ArrayMap(key为对象)
- 枚举优化
- 缺点:
1、每一个枚举值都是一个单例对象,在使用它时会增加额外的内存消耗,所以枚举相比与 Integer 和 String 会占用更多的内存。
2、较多的使用 Enum 会增加 DEX 文件的大小,会造成运行时更多的IO开销,使我们的应用需要更多的空间。
3、特别是分dex多的大型APP,枚举的初始化很容易导致ANR
- 缺点:
- 改进将常量值变成
public static final int RECTANGLE=0;
public static final int TRIANGLE=1;
public static final int SQUARE=2;
public static final int CIRCLE=3;
//flag=true ,只能选择常量的一种负值不能| false 则可以|
@IntDef(flag=true,value={RECTANGLE,TRIANGLE,SQUARE,CIRCLE})
@Target({ElementType.PARAMETER,ElementType.METHOD,ElementType.FIELD})
@Retention(RetentionPolicy.SOURCE)
public @interface Model{
}
private @Model int value=RECTANGLE;
public void setShape(@Model int value){
this.value=value;
}
@Model
public int getShape(){
return this.value;
}
- static static final,static会由编译器调用clinit方法进行初始化
static final不需要进行初始化工作,打包在dex文件中可以直接调用,并不会在类初始化申请内存
所以基本数据类型的成员,可以全写成static final - 字符串的连接尽量少用加号(+) 使用StringBuffer
- 循环中避免重复申请内存,对象放到循环外面
- 避免GC回收之后还会用的对象,使用对象池来处理