您的位置:首页 > 其它

高效实用内存

2015-08-24 16:01 274 查看
         Android设备和其上应用使用的内存是有限的,使用尽可能少的内存,既是经验也是常识。此外,除了减少碰到OutOfMemoryError异常的风险,使用尽量少的内存也可以提升性能。

        性能主要取决于以下三个因素:

       1. CPU如何操作特定的数据类型。

       2. 数据和指令需要占用多少指令空间。

       3. 数据在内存中的布局。

  

        数据类型

        1.  使用较小的数据类型,并不总会提升性能,甚至可能需要更多的指令。所以在处理大量数据时,使用可以满足要求的最小的数据类型。

        2.  使用long看起来比使用short和int慢。同样,只使用double及混用float和double,似乎比只用float慢。

        3.  short数组排序远快于其他类型数组。(在不超过float取值范围-32768~32767的情况下,尽量使用short类型)。

        4.  处理64位类型(double或者float)比处理32位类型慢。

        5.  尽量避免类型转换。

        6.  如果有必要去的更好的性能,推倒重来,但要认真处理。

        这些规则不是一成不变的,但是,多半情况下,使用较少内存的原则是指的遵守的。此外,这样会留出更多的内存给其他的任务,使用较少的内存还可以提高性能,因为这样可以提高CPU缓存的使用率,更快地访问数据或者指令。

        访问内存

         因为访问内存会产生一些开销,CPU会把最近范文的内存内从缓存起来,无论是内存读还是内存写。事实上,CPU通常分为两级缓存:

                #  一级缓存(L1)

                #  二级缓存(L2)

         一级缓存通常比二级缓存快,但是比二级缓存小。例如,L1缓存可能是63kb(32kb数据缓存32kb指令缓存),而L2可能是512kb。(一些处理器可能还有三级缓存,通常几兆字节,但是目前的嵌入式设备上还没有)

        当数据或者指令在缓存中找不到时,就是缓存未命中。这时候需要从内存中去除数据或者指令。缓存命中有一下几种情况:

                 # 指令缓存读未命中

                 # 数据缓存读未命中

                 # 写未命中

        第一种缓存未命中最关键,因为CPU要一直等到从内存中读出指令,才可以继续执行。第二种缓存未命中几乎和第一种同样重要,尽管CPU仍可能执行其他不依赖要读取数据的指令,但这种情况只会在CPU指令乱序执行时出现。最后一种缓存未命中的重要性最低,CPU通常可以继续执行指令。你几乎无法控制写命中,不过无需过于担心,只要关心前两中类型就好,它两是应该激励避免的缓存命中情况。

         除了总大小,缓存的另一个重要的属性石其行大小。缓存由行组成,每行包含几个字节,如Contex A8的缓存行大小为64个字节(16个字)。缓存和缓存行背后的思想是局部性原则:如果应用读或者写入了一个特定的地址,将来很可能会再次读取或者写入到相同的地址或者非常邻近的地址。一下方法可以减少指令缓存未命中的几率:

        #  在Thumb模式下编译本地库。这不保证会使代码速度更快,因为Thumb指令码,比ARM指令码执行得慢(可能要执行更多的指令)。

        #  保持代码相对密集。 虽然不能保证密集的Java代码会产生密集的机器码,但是这往往是可行的。

        以下方法可以减少数据缓存的未命中率:

        #  在有大量数据存储在数组中时,使用尽量小的数据类型。

        # 选择顺序访问而不是随机访问。最大限度地重用已在缓存中的数据,防止数据从缓存清除后再次载入。

 

        排布数据

        1.  打破封装原则。尽管创建对象时轻量级的,但是内存分配还是会产生开销,而且这是不可避免的。如果属性是公有的,就可以避免getter方法的开销。

         这种原则可能带来维护上的问题,而不能正确地优化代码。因此要本着务实的态度来优化:在找到解决问题的关键点之前,不要优化,针对一种使用方式的优化,可能给其他使用方式带来劣化。         

        

         垃圾收集

        尽管Java可以自动进行垃圾回收,但是还是要注意:

         #  还是有可能出现内存泄露。

         #  垃圾收集会帮你管理内存(通过不同的垃圾收集器实现算法管理内存),它做的不仅仅是释放不用的内存。

 

         内存泄露

         只有当某个对象不再被引用时,它的内存才会被回收,当该被释放的对象应用依然存在时,就会发生内存泄露。在Android文档中看到的一个典型的例子是,由于屏幕旋转整个Activity对象会有泄露。这个典型的例子很容易重现,也是相当严重的,因为Activity对象占用相当多的内存(往往包含多个对象的应用)。避免内存泄露没有简单的解决方案,但Android提供的工具和API可以为你提供帮助。

         Eclipse中的DDMS视图中,可以用Heap和Allocation Tracker跟踪内存使用和分配情况,但这些工具是不会告诉你是否存在内存泄露的,你可以用他们来分析应用的内存使用情况。并有希望找出应用中的内存泄露。

         Android2.3中定义了StrictMode类,它对检测潜在的内存泄露有很大帮助。虽然在Android2.3中,StrictMode的虚拟机策略只能检测SQLite对象是否关闭是产生的泄露,但是在Android3.0及以上版本中,可以检测出一下潜在的泄露:

         #  Activity泄露;

         #  其他对象泄露;

         #  对象没有关闭造成的泄露(至于是哪些对象,可以查看文档中的Closeable接口的所有类);

        引用类型

        内存释放是垃圾收集器的一个重要的特性,在垃圾收集器中他的作用比在内存管理系统中重要的多。事实上,Java定义了四中类型的引用:

              #  强引用  ( Strong )

              #  软引用  ( Soft )

              #  弱引用  ( Weak )

              #  虚应用  ( Phantom )

         强引用: 程序中大部分都是用这种引用,强引用就是正常引用,简称为“引用”。

         软、弱、虚引用: 软引用和弱引用的本质是相似的,他们是没有抢到足以保持对象不被删除或回收的引用。不同之处在于回收时,垃圾收集器处理他们的对象的积极程度不同。

         软可及对象,既存在一个软引用,但是没有强引用,当有足够的内存保留对象时,垃圾收集器不会回收它。不过,如果垃圾收集器决定需要回收更多的内存,那么它可任意回收软可及对象的内存。这种引用适合于缓存,它可以自动删除缓存中的条目。

        弱可及对象,也就是说,存在一个弱引用,但没有强或者软引用,下次垃圾回收时基本上会被收走。换言之,垃圾收集器会更加积极地回收弱可及对象的内存。这种类型的引用适用于映射,这种映射可以自动删除不在被引用的键,WeakHashMap类就是这样做的。

        虚引用最弱,几乎很少用到。如果你的应用需要知道一个对象什么时候被回收,并需要在回收的同时执行一些清理工作,可以使用它们。虚引用够可以用来注册引用队列。

        软、弱、虚引用实际上是在对象本身和其他对象间加了一个间接层。

  

        垃圾收集

        垃圾收集可能会在不定的时间触发,你几乎无法控制它发生的时机。有时可以通过System.gc()提醒一下Android,但垃圾收集的发生时间最终由Dalvik虚拟机决定。有以下五种情况会触发垃圾收集:

        # GC_FOR_MALLOC : 发生在堆被占满,不能进行内存非配时,在分配新对象之前进行内存回收。

        # GC_CONCURRENT : 发生在(可能是部分的)垃圾可供回收时,通常有很多对象可以回收。

        # GC_EXPLICIT : 现实调用System.gc()产生的垃圾收集。

        # GC_EXTERENAL_ALLOC : Honeycomb及以上版本不会出现(一切都已在堆中分配)

        # GC_HPROF_DUMP_HEAP :  发生在创建HPROF文件时。

        在Android2.2或更早之前的版本中,垃圾收集发生在应用的主线程中,可能降低应用的响应速度和性能,造成恶劣影响。从Android2.3开始,有了转机,垃圾收集工作转移到了一个单独的线程进行。

    

        内存少的时候

        当内存不足以分配给所有程序的情况下,Android会要求应用及应用的组件(Activity,Fragment等)勒紧腰带。

        ComponentCallbacks接口定义了API onLowMemory(), 它对所有应用组件都是相同的。当它调用时,组件基本会被要求释放那些并不会用到的内存。通常情况下,onLowMemory()的实现将释放

        #  缓存或缓存条目(如使用强引用的LruCache);

        #  可以再次按需生成位图对象;

        #  不可见的布局对象;

        #  数据库对象;

        
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: