Android高手课----内存优化1

      Android中的内存优化是我们性能优化中比较重要的一环,其主要包括两方面的工作:

  1. 优化RAM(Random Access Memory 也即主存,是与CPU直接交换数据的内部存储器,可以随时读写,而且速度快),即降低运行时内存。目的是防止程序发生OOM异常,以及降低程序由于内存过大被LMK机制杀死的概率。另外,不合理的内存使用会使GC大大增多,从而导致程序变卡。
  2. 优化ROM(Read-Only Memory 只能读出事先所存储的数据,之后无法改变或删除),即降低程序占ROM的体积,这里主要是为了降低程序占用的空间,防止由于ROM空间不足导致程序无法安装。

内存泄漏的监控方案:

     Square的开源库leakcanary是一个不错的选择,其原理是,通过弱引用方式侦查Activity或对象的生命周期(将Activity或对象包装到WeakReference中,通过监听Application中activity生命周期回调,声明周期结束时,如果对象能被正常回收,弱引用会被放到ReferenceQueue队列里),若发现内存泄漏自动dump hprof文件,通过HAHA库得到泄漏的最短路径,最后通过notification展示。

通过兜底回收内存:

    Activity泄漏会导致该Activity引用到的Bitmap、DrawingCache等无法释放,对内存造成大的压力,兜底回收是指对于已泄漏的Activity,尝试回收其持有的资源,泄漏的仅仅是一个Activity空壳,从而降低对内存的压力。做法也很简单,在Activity onDestory时候从view 的rootView开始,递归释放所有子view涉及的图片、背景、DrawingCache、监听器等资源,让Activity成为一个不占资源的空壳,泄漏了也不会导致图片资源被持有。

Drawable d = iv.getDrawable();

if(d != null){

    d.setCallback(null);
}

iv.setImageDrawable(null);

    我们不是只懂得一些内存泄漏解决方法就可以,更重要的是通过日常测试与监控,得到内存泄漏检测与修改的一整套闭环体系。

降低运行时内存的一些方法:当确保应用中不会出现内存泄漏后,我们还需要一些方法降低运行时内存,其实也是希望降低OOM的概率。

一、减少bitmap占用的内存。

  1. 防止bitmap占用资源太多导致OOM,可采用Facebook的fresco库,即可把图片资源放于Native中。
  2. 图片按需加载。即图片的大小不应该超过view的大小,在把图片载入内存之前,我们需要先计算出一个合适的inSampleSize缩放比例,避免不必要的大图载入。对此,我们可以重载drawable与ImageView,例如在Activity onDestroy时,检测图片大小与view的大小,若超过,可以上报或提示。
  3. 统一的bitmap加载器。有了统一的加载库,如果在加载时方式OOM,可以通过清除cache,降低bitmap format等方式,重新尝试。
  4. 图片存在像素浪费。对于.9图,美工可能在出图时拉伸与非拉伸区域都有大量的像素重复,通过获取图片的像素ARGB值,计算连续相同的像素区域,自定义算法判断这些区域是否可以缩放。关键也是需要将这些工作做到系统化,可及时发现问题,解决问题。

二、自身内存占用监控。

    dalvik内存距离OOM的差值并没有体现,也没有回调函数供我们及时释放内存。如果能有一套机制,可是实时监控堆内存的使用率,达到设定值即通知相关模块进行释放,这会大大的降低OOM。

    原理:通过Runtime获得maxMemory,而totalMemory-freeMemory即为当前真正使用的dalvik内存。

Runtime.getRuntime().maxMemory();
Runtime.getRuntime().totalMemory()-Runtime.getRuntime().freeMemory();

   操作方式:我们可以定期(前台每隔3分钟)的去得到这个值,当我们这个值达到危险值(例如80%),我们应当主要去释放我们的各种cache资源(bitmap的cache为大头),同时显示的去trim应用的memory,加速内存收集。

WindowManagerGlobal.getInstance().startTrimMemory(TRIM_MEMORY_COMPLETE);

三、使用多进程

    对于WebView、图库等,由于存在内存泄漏或者占用过多的问题,我们可以采取单独的进程,微信会把它放在单独的tools进程中。

四、上报OOM详细信息

    当系统放生OOM的crash时,我们应当上传更加详细的内存相关信息,方便我们定位当前内存的具体情况。

GC优化

   大量的GC操作则会显著占用帧间隔时间(16ms),如果在帧间隔时间里做了过多的GC操作,那么其他类似计算、渲染等操作的可用时间就变得少了。

一、GC类型有以下几种,其中GC_FOR_ALLOC是同步方式进行的,对应用帧率的影响最大:

  1. GC_FOR_ALLOC:当堆内存不够的时候容易触发,尤其是new一个对象的时候,很容易被触发到,所以如果要加速启动,可以提高dalvik.vm.heapstartsize的值,这样在启动的过程中可以减少GC_FOR_ALLOC的次数。注意这个触发是以同步的方式进行的,如果GC后仍然没有空间,则堆进行扩张。
  2. GC_EXPLICIT:这个gc是可以被调用的,比如system.gc,一般gc线程的优先级比较低,所以这个垃圾回收的过程不一定会马上触发,千万不要认为调用了system.gc,内存的情况就有所好转。
  3. GC_CONCURRENT:当分配的对象大小超过384k时触发,注意这是以异步的方式进行回收的,如果发现大量反复的Concurrent GC出现,说明系统中可能一直有大于384k的对象被分配,而这些往往是一些临时对象,被反复触发了。给我们的暗示是:对象的复用不够。

二、内存抖动

        Memory Churn,内存抖动是因为在短时间内大量的对象被创建又马上被释放。瞬间产生大量的对象会严重占用内存区域,当达到阀值,剩余空间不够的时候,会触发GC从而导致刚产生的对象又很快被回收。即使每次分配的对象占用了很少的内存,但是他们叠加在一起会增加Heap压力,从而触发更多其他类型的GC。这个操作有可能会影响到帧率,并使得用户感知到性能问题。

      通过Memory Monitor(Android Studio3.0以后,被Android Profiler替代),我们可以跟踪整个APP的内存变化情况,若短时间内发生了多次内存的涨跌,这意味着很有可能发生了内存抖动。

三、GC优化

    通过Heap Viewer,我们可以查看当前内存快照,便于对比分析哪些对象又可能发生了泄漏。更重要的工具是Allocation Tracker,追踪内存对象的类型、堆栈、大小等。这样,我们就可以快速的知道发生内存抖动时,是因为哪些变量的创建造成频繁GC。一般来说我们需要注意以下几个方面:

  1. 字符串拼接优化:减少字符串使用加号拼接,改为使用StringBuilder。减少StringBuilder.enlarge,初始化时设置capacity;
  2. 读文件优化:读文件使用ByteArrayPool,初始设置capacity,减少expand;
  3. 资源重用:建立全球资源池,对频繁申请、释放的对象类型重用;
  4. 减少不必要或不合理的创建对象:例如在onDraw、getView中应减少对象申请,尽量重用。更多是一些逻辑上的东西,例如循环中不断申请局部变量等。
  5. 选用合理的数据格式:使用SparseArray、LongSparseArray、SparseBooleanArray、SparseLongArray来代替HashMap。

 

Tip:SparseArray的key只能是int,LongSparseArray的key是long,能够存取的范围更大。其优缺点有如下,

优点:

  1. 避免存取元素时的拆箱、装箱,提高了效率。
  2. 频繁的插入和删除操作时效率高。
  3. 会定期通过gc来清理内存,内存利用率高。
  4. 放弃hash查找,利用二分查找,更轻量。

缺点:

  1. 二分查找时间复杂度为O(log n),没有HashMap效率高。
  2. key只能是int 或者long,而HashMap为Object。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值