性能分析之内存优化

一,内存分析

1,Java堆中各代的分布:

image2016-5-22%2014%3A11%3A43.png?versio
      
Young:主要是用来存放新生的对象
   
Old:主要存放应用程序中生命周期长的内存对象
   
Permanent:是指内存的永久保存区域,主要存放Class和Meta的信息
    
    
在Young中存放的对象都是会快速被创建并且很快被销毁回收的,同时这个区域的GC操作速度也是比Old Generation区域的GC操作速度更快的。
     
除了速度差异之外,执行GC操作的时候,所有线程的任何操作都会需要暂停,等待GC操作完成之后,其他操作才能够继续运行。
    
通常来说,单个的GC并不会占用太多时间,但是大量不停的GC操作则会显著占用帧间隔时间(16ms)。如果在帧间隔时间里面做了过多的GC操作,那么自然其他类似计算,渲染等操作的可用时间就变得少了。
     

2,分析工具

image2016-5-22%2014%3A12%3A0.png?version
     
The Heap and CPU Profiling Agent (HPROF)是JAVA2 SDK自带的一个简单的profiler代理,它通过与Java Virtual Machine Profiler Interface (JVMPI) 交互,将profiling信息通过本地文件或socket输出ASCII或二进制格式的流。
    
HPROF可以监控CPU使用率,堆分配统计。除此之外,还可以报告JVM所有监视器和线程的完整的堆的dump状态。
     
1),分析hprof可以用AS自带的和MAT.
    
下面是AS自带的分析工具:
    
    
image2016-5-22%2014%3A12%3A58.png?versio
      
2),用Android Tools中的Heap Viewer进行分析当前进程中的Heap Size情况
    
image2016-5-22%2014%3A13%3A14.png?versio
    
3),Memory Churn
    
Memory Churn因为在短时间内大量的对象被创建又马上被释放。瞬间产生大量的对象会严重占用Young Generation的内存区域,当达到阀值,剩余空间不够的时候,会触发GC从而导致刚产生的对象又很快被回收。即使每次分配的对象占用了很少的内存,但是他们叠加在一起会增加Heap的压力,从而触发更多其他类型的GC。这个操作有可能会影响到帧率,并使得用户感知到性能问题。
    
要定位Memory Churn,可以利用AS中的Memory Monitor很方便的分析,具体方法更上面分析Memory Leak一样
   
上面提到了4种内存分析工具:
    
A,Memory Monitor:跟踪整个app的内存变化情况
    
B,Heap Viewer:查看当前内存快照,分析哪些对象可能发生泄漏
   
C,Allocation Tracker:最终内存对象的来源
   
D,MAT:Eclipse的内存分析器,能快速对Java堆进行分析。
   
4),命令查看当前应用使用内存的情况
   
image2016-5-22%2014%3A13%3A33.png?versio
    
5),静态检查工具也能帮我们找到一些泄漏点,如:Lint
    

二,内存泄漏经验点

1,Activity生命周期超越activity对象的生命周期

     

(1)避免在Activity里面实例化其非静态内部类的静态实例 (尤其对launchMode不是singleInstance)
    
(2)用全局的Context代替组件的Context
     
(3)避免使用非静态内部类在activity中
    
         推荐方式:使用一个静态的内部类,并且对它的外部类用弱应用(WeakReference)
   
         典型案例:ViewRootImpl 中的W内部类
    
             image2016-5-22%2014%3A13%3A53.png?versio

2,Handler导致的Activity泄漏

   
     如果Handler中有延迟的任务或者是等待执行的任务队列过长,都有可能因为Handler继续执行而导致Activity发生泄漏。此时的引用关系链是Looper -> MessageQueue -> Message -> Handler -> Activity
    
     解决方案:1)在UI退出之前,执行remove Handler消息队列中的消息与runnable对象
    
                      2)将Handler定义为静态内部类,并且在类中增加一个成员变量,用WeakReference来应用外部实例
      
   

3,HandlerThread使用时需要在结束时调用mThread.getLooper().quit()

    

4,注册某一对象后未反注册

   

5,集合对象也要及时清理

  

6,资源对象用完需要及时关闭

  

7,构建Adapter时,使用缓存的convertView

   

8,不要在经常调用的方法中创建对象(如:循环,onDraw等),要巧用容器,池

   

9,使用更加轻量的数据结构(如:用ArrayMap / SparseArray来替换HashMap)

  

10,尽量避免使用Enum

   

11,大量字符串的拼接使用StringBuilder

   

12,谨慎使用large heap

   

13,onLowMemory()与onTrimMemory()

     

       onLowMemory : Android系统提供了一些回调来通知当前应用的内存使用情况,通常来说,当所有的background应用都被kill掉的时候,forground应用会收到onLowMemory()的回调。在这种情况下,需要尽快释放当前应用的非必须的内存资源,从而确保系统能够继续稳定运行。
           
       onTrimMemory : Android系统从4.0开始还提供了onTrimMemory()的回调,当系统内存达到某些条件的时候,所有正在运行的应用都会收到这个回调,同时在这个回调里面会传递以下的参数,代表不同的内存使用情况,收到onTrimMemory()回调的时候,需要根据传递的参数类型进行判断,合理的选择释放自身的一些内存占用,一方面可以提高系统的整体运行流畅度,另外也可以避免自己被系统判断为优先需要杀掉的应用。
   
 

14,资源文件使用Android提供合适的文件夹存放

   

 
     hdpi/xhdpi/xxhdpi等等不同dpi的文件夹下的图片在不同的设备上会经过scale的处理。例如我们只在hdpi的目录下放置了一张100100的图片,那么根据换算关系,xxhdpi的手机去引用那张图片就会被拉伸到200200。需要注意到在这种情况下,内存占用是会显著提高的。对于不希望被拉伸的图片,需要放到assets或者nodpi的目录下
    

15,使用IntentService,它会在处理完交代给它的任务之后尽快结束自己

   

16,优化布局层次,减少内存消耗

    

 
       越扁平化的视图布局,占用的内存就越少,效率越高。我们需要尽量保证布局足够扁平化,当使用系统提供的View无法实现足够扁平的时候考虑使用自定义View来达到目的
  

17,谨慎使用多进程

 
   
      使用多进程可以把应用中的部分组件运行在单独的进程当中,这样可以扩大应用的内存占用范围,但是这个技术必须谨慎使用,绝大多数应用都不应该贸然使用多进程,一方面是因为使用多进程会使得代码逻辑更加复杂,另外如果使用不当,它可能反而会导致显著增加内存。当你的应用需要运行一个常驻后台的任务,而且这个任务并不轻量,可以考虑使用这个技术

18,谨慎使用第三方libraries

 

19,考虑不同的实现方式来优化内存占用

 
      有时我们需要结合整体项目评估内存使用方案,若一味的降低内存而不综合考虑不一定是最好。

20,图片的处理

     

图片一般是APP处理的大头,如处理不好会极其造成大内存占用,影响性能,轻则越用越慢,重则崩溃闪退,严重影响用户体验。
   
对于图片的处理一般可以采取如下方式处理:
    
(1)decode format : 不同的解码格式最内存的占用也存在很大差异
            
          ALPHA_8 : 8位的alpha位图
   
          ARGB_4444 : 16位ARGB位图
   
          ARGB_8888 :  32位ARGB位图
   
          RGB_565:代表8位RGB位图
   
            
      位图位数越高代表其可以存储的颜色信息越多,当然位图也就越逼真但同时占用的内存也会越多。我们需要结合项目实际灵活选择
   
(2)inSampleSize : 缩放比,在载入内存前,我们可以计算出一个合适的缩放比例,避免不必要的大图载入
     
(3)Decode图片时采用Native memory内存占用空间
          
         这里面有2种实现方式:第一种可以用JNI在底层结合SKIA图片库进行处理;第二种利用Android提供的API处理
   
         上面的第一种方式难度会比较大,且有各种兼容性问题处理,这里重点介绍第二种,其实Android很早就已经考虑到这个问题了,在5.0以前可以如下API配合使用:
     
         options.inPurgeable = true;
   
         options.inInputShareable = true;
   
(4)图片使用完要及时进行回收操作
    
         在释放资源时,需要注意释放的Bitmap或者相关的Drawable是否有被其它类引用。如果正常的调用,可以通过Bitmap.isRecycled()方法来判断是否有被标记回收;而如果是被UI线程的界面相关代码使用,就需要特别小心避免回收有可能被使用的资源,不然有可能抛出系统异常 cannot draw recycled bitmaps
   
(5)不必要的时候避免图片的完整加载
   
        只需要知道图片大小的情形下,可以不完整加载图片到内存。 在使用BitmapFactory压缩图片的时候,BitmapFactory.Options设置inJustDecodeBounds为true后,再使用decodeFile()等方法,可以在不分配空间状态下计算出图片的大小
  
       如:我们的计算图片大小的时候就需要设置此属性为true,不然就会完整加载图片而浪费内存
   
(6)尽可能使用更小的图片(可以根据不同的分辨率使用不同的图片)
   
       尽量使用更小的图片不仅仅可以减少内存的使用,还可以避免出现大量的InflationException。假设有一张很大的图片被XML文件直接引用,很有可能在初始化视图的时候就会因为内存不足而发生InflationException,这个问题的根本原因其实是发生了OOM
      
 
(7)图片缓存(对象池)
    
       在Android上面最常用的一个缓存算法是LRU(Least Recently Use)。
    
       目前比较常用成熟的应用框架有:ImageLoader, Fresco等,当然也可以自实现,一般采用3级缓存机制,然后结合设备内存阀值机其他因素灵活结合LRU算法即可
   
(8)复用系统自带的资源
    
      Android系统本身内置了很多的资源,例如字符串/颜色/图片/动画/样式以及简单布局等等,这些资源都可以在应用程序中直接引用。但是也有必要留意Android系统的版本差异性,对那些不同系统版本上表现存在很大差异,不符合需求的情况,还是需要应用程序自身内置进去
    
(9)利用inBitmap的高级特性提高Android系统在Bitmap分配与释放执行效率上的提升
    
      使用inBitmap属性可以告知Bitmap解码器去尝试使用已经存在的内存区域,新解码的bitmap会尝试去使用之前那张bitmap在heap中所占据的pixel data内存区域,而不是去问内存重新申请一块区域来存放bitmap。利用这种特性,即使是上千张的图片,也只会仅仅只需要占用屏幕所能够显示的图片数量的内存大小
   
      使用这个API有几个点需要注意:
  
       A,在SDK 11 -> 18之间,重用的bitmap大小必须是一致的,例如给inBitmap赋值的图片大小为100-100,那么新申请的bitmap必须也为100-100才能够被重用。从SDK 19开始,新申请的bitmap大小必须小于或者等于已经赋值过的bitmap大小
   
       B,新申请的bitmap与旧的bitmap必须有相同的解码格式,例如大家都是8888的,如果前面的bitmap是8888,那么就不能支持4444与565格式的bitmap了。 
   
我们可以创建一个包含多种典型可重用bitmap的对象池,这样后续的bitmap创建都能够找到合适的“模板”去进行重用
   
(10)使用inNativeAlloc来达到扩大使用内存的目的 (限4.0以下)
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值