内存优化(1)

1,分析内存的常用工具

top/procrank
meinfo
Procstats
DDMS
MAT
Finder-Activity
LeakCanary
LeakInspector
工具很多,掌握原理方法,工具随便找两个能用就行

2,java虚拟机

1562658885001.png
1562659057852.png

线程私有

程序计数器pc:

相当于一个执行diam的指示器,用来确认下一行执行的代码的地址,每个线程都有一个,没有OOM的区域

虚拟机栈:

我们平时说的栈就是这块区域。java虚拟机规范中定义了outofmemory和stackoverflow异常

本地方法栈:(Native方法)

java虚拟机规范中定义了outofmemory和stackoverflow异常

共享数据区

方法区

ClassLoader加载类信息,常量,静态变量,编译后的代码

OOM

运行时常量池:

字面量 public satic final java常量,

符号引用 类,接口全名,方法名

java堆

虚拟机能管理的最大的一块内存 GC的主战场

OOM

对象实例,数组的内容

3,GC垃圾回收器

gc如何确定内存回收?

引用计数算法:

Object o1=new Object(); 计数+1=1
Object o2;
o2=o1; 计数+1=2
o1=null; 计数为1 o1和o2都不会回收

缺点:互相引用容易出现计算器永不为0

可达性分析算法:

1562660363424.png

可作为GC ROOT的对象 :虚拟机栈正在运行使用的引用
静态属性 常量
JNI引用的对象

GC是需要2次扫描才回收对象,所以我们可以用finalize去救活丢失引用的对象
例如:
static App a;
@Override
protected void() throws Throwable{
​ super.finalize();
​ a=this;
}

回收也和引用类型有关系:

强引用 : Object obj=new Object();

软引用SoftReference : 内存不足时回收,存放一些重要性不是很强又不能随便让清除的对象,比如图片切换到后台不需要马上显示了

弱引用WeakReference:第一次扫到了,就标记下来,第二次扫到直接回收

虚引用PhantomReference:幽灵 幻影引用 不对生存造成任何影响,用于跟踪GC的回收通知

4.内存泄漏

产生的原因:

一个长生命周期的对象持有一个短生命周期对象的引用
通俗讲就是该回收的对象,因为引用问题没有被回收,最终会产生OOM

Android Profiler的使用 :

AndroidStudio Run菜单下的profile...

在图型用户界面上选择要分析的一段内存,右键export出来

Allocations: 动态分配对象个数

Deallocation:解除分配的对象个数

Total count :对象的总数

Shallow Size:对象本身占用的内存大小

Retained Size:GC回收能收走的内存大小

Mat工具的使用 :

转换profile文件格式 sdk/platform-tools/hprof-conv.exe

转换命令 hprof-conv -z src dst

打开软件 File菜单下Open Heap Dump... 打开转换好的文件

引发原因:

第三方SDK引发的问题

Android源码引发的问题

常见的错误代码引起的问题

5,内存抖动

内存频繁的分配与回收,(分配速度大于回收速度时,最终会产生OOM)

回收算法:

标记清除算法

1562897973097.png

复制算法

1562898047989.png

标记压缩算法

1562898081653.png

分代回收算法

1562903100905.png

对象将根据存活的时间被分为:年轻代(Young Generation)、年老代(Old Generation)、永久代(Permanent Generation,也就是方法区)

年轻代(Young Generation)

绝大多数刚创建的对象会被分配在Eden区,其中的大多数对象很快就会消亡。Eden区是连续的内存空间,因此在其上分配内存极快;当Eden区满的时候,执行Minor GC,将消亡的对象清理掉,并将剩余的对象复制到一个存活区Survivor0(此时,Survivor1是空白的,两个Survivor总有一个是空白的);此后,每次Eden区满了,就执行一次Minor GC,并将剩余的对象都添加到Survivor0;当Survivor0也满的时候,将其中仍然活着的对象直接复制到Survivor1,以后Eden区执行Minor GC后,就将剩余的对象添加Survivor1(此时,Survivor0是空白的)。当两个存活区切换了几次之后,仍然存活的对象,将被复制到老年代。
从上面的过程可以看出,Eden区是连续的空间,且Survivor总有一个为空。经过一次GC和复制,一个Survivor中保存着当前还活 着的对象,而Eden区和另一个Survivor区的内容都不再需要了,可以直接清空,到下一次GC时,两个Survivor的角色再互换。因此,这种方 式分配内存和清理内存的效率都极高,这种垃圾回收的方式就是复制算法(将Eden区和一个Survivor中仍然存活的对象拷贝到另一个Survivor中)。

年老代(Old Generation)

对象如果在年轻代存活了足够长的时间而没有被清理掉(即在几次 Young GC后存活了下来),则会被复制到年老代,年老代的空间一般比年轻代大,能存放更多的对象,在年老代上发生的GC次数也比年轻代少。当年老代内存不足时, 将执行Major GC,也叫 Full GC。  
如果对象比较大(比如长字符串或大数组),Young空间不足,则大对象会直接分配到老年代上(大对象可能触发提前GC,应少用,更应避免使用短命的大对象)。
可能存在年老代对象引用新生代对象的情况,如果需要执行Young GC,则可能需要查询整个老年代以确定是否可以清理回收,这显然是低效的。解决的方法是,年老代中维护一个512 byte的块——”card table“,所有老年代对象引用新生代对象的记录都记录在这里。Young GC时,只要查这里即可,不用再去查全部老年代,因此性能大大提高。

永久代(Permanent Generation方法区)

永久代的回收有两种:常量池中的常量,无用的类信息,常量的回收很简单,没有引用了就可以被回收。

垃圾收集器

CMS收集器(标记-清除)

1562901270608.png

CMS采用"标记-清除"算法实现以获取最短回收停顿时间为目标的收集器

初始标记:标记一下GC Roots能直接关联到的对象

并发标记:进行GC Roots Tracing 的过程

重新标记:是为了修正并发标记期间因用户程序继续运行而导致标记产品变动的那一部分对象的标记记录

并发清除:清除不能到达GC Roots的对象

重置线程:更新之前使用过的数据

优点:并发收集,低停顿

缺点:不能处理浮动垃圾,消耗cpu资源多,产生大量碎片

6,优化内存的良好编码习惯

1.数据类型

不要使用比需求更占空间的基本数据类型

2.循环尽量用foreach,少用iterator, 自动装箱尽量少用

3.数据结构与算法的解度处理

数组,链表,栈,树,图。。。。

数据量千级以内可以使用
Sparse数组(key为整数),ArrayMap(key为对象)
性能不如HashMap但节约内存

4.枚举优化

每一个枚举值都是一个单例对象,在使用它时会增加额外的内存消耗,所以枚举相比与 Integer 和 String 会占用更多的内存

枚举可以进行改进

public enum SHAPE{
​ RECTANGLE,
​ TRIANGLE,
​ SQUARE,
​ CIRCLE
}
public class SHAPE{
​ public static final int RECTANGLE=0;
​ public static final int TRIANGLE=1;
​ public static final int SQUARE=2;
​ public static final int CIRCLE=3;
}

5.static staticfinal的问题

static会由编译器调用clinit方法进行初始化
static final不需要进行初始化工作,打包在dex文件中可以直接调用,并不会在类初始化申请内存
所以基本数据类型的成员,可以全写成static final

6.字符串的连接尽量少用加号(+),尽量使用stringBuilder

7.重复申请内存的问题

同一个方法多次调用,如递归函数 ,回调函数中new对象,读流直接在循环中new对象等
不要在onMeause() onLayout() onDraw() 中去刷新UI(requestLayout)

8.避免GC回收将来要重用的对象

内存设计模式对象沲+LRU算法

9,Activity组件泄漏

非业务需要不要把activity的上下文做参数传递,可以传递application的上下文

和Activity有关联的对象写成static 如private static Button btn; private static Drawable drawable;

非静态内部类和匿名内部内会持有activity引用,建议大家单独写个文件

单例模式持有activity引用(使用weakRefrence)

handler.postDelayed()问题

如果开启的线程需要传入参数,用弱引接收可解决问题,handler记得清除removeCallbacksAndMessages(null)

10.尽量使用IntentService,而不是Service

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,657评论 6 505
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,889评论 3 394
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,057评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,509评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,562评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,443评论 1 302
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,251评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,129评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,561评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,779评论 3 335
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,902评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,621评论 5 345
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,220评论 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,838评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,971评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,025评论 2 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,843评论 2 354

推荐阅读更多精彩内容