Android内存管理及优化


Random Access Memory(RAM)在任何软件开发环境中都是非常重要的资源,但在物理内存通常很有限的移动操作系统上更为重要。尽管Android Runtime (ART)和Dalvik虚拟机扮演了垃圾回收的角色,但这并不意味着我们可以忽略应用的内存分配与释放的时机和位置。我们也需要避免引入Memory Leaks(内存泄漏,通常由于在Gm需要回收一些释放对象时扔持有对象引用在静态成员变量里,在释放一些引用对象在需要回收时)。

本文会解释Android是如何管理应用的内存分配,以及在开发Android应用的时候如何主动的减少内存的使用。

1、Android是如何管理内存的

Android的运行时(ART)和Dalvik虚拟机使用分页和内存映射(mmapping)来管理内存。这意味着,一个应用程序的任何内存修改,不管是通过分配新的对象还是触摸mmapped页,仍驻留在内存中,不能被调出。从应用程序释放内存的唯一方法是释放应用所持有的引用对象,使内存可用于垃圾收集器。有一个例外:没有修改mmapped中的任何文件,如代码,在系统在其他地方需要使用该内存时被换出RAM。

1. Garbage collection(垃圾回收)
一个管理内存的环境,如ART或Dalvik虚拟机,跟踪每一个内存分配。一旦它确定一块存储器不再被使用的程序,它释放回堆,不需要开发者的任何干预。对于托管内存环境中回收未使用的内存的机制被称为Garbage collection(垃圾回收)垃圾回收有两个目标:找到程序里未来不能被访问的对象并回收这些对象所使用的资源。

2.shared memory(共享内存)
为了适应在RAM中需要的一切,Android尝试共享跨进程内存的pages。它可以通过以下方式做到:

  • 每一个app的process都是从Zygote(受精卵)的进程中fork出来的。Zygote进程在系统启动并且加载通用的framework的代码与资源(比如ActivityThemes)时开始启动。为了启动一个新的程序进程,系统会fork Zygote进程生成一个新的process,然后在新的process中加载并运行app的代码。这使得大多数的RAM pages被用来分配给framework的代码与资源,并在应用的所有进程中进行共享。
  • 大多数static的数据被映射到一个进程中。这种方式可以使数据不仅能够在进程间进行共享,而且使得它能够在需要的时候被paged out。例如下面几种static的数据:
    • Dalvik code (by placing it in a pre-linked .odex file for direct mmapping

    • App resources (by designing the resource table to be a structure that can be mmapped and by aligning the zip entries of the APK)

    • Traditional project elements like native code in .so files.

    • 在很多地方,Android通过分配的共享内存(例如ashmem(匿名共享内存)或者gralloc)来实现跨进程共享内存。例如,window surfaces在app与screen compositor之间使用共享的内存,cursor buffers在content provider与client之间使用共享的内存。

3.分配和回收应用内存
每一个应用进程的Dalvik 都与一个虚拟的内存范围(heapSize)。这定义了一个了了逻辑堆的大小(heapSize),它可以随着需要增长至系统为每个应用设定的大小限制。

heapSize和实际物理上的内存数量是不等的。当检查app的堆时,Android会计算Proportional Set Size(PSS)的值,PSS是实际共享的内存大小,记录了那些和其他进程进行共享的内存。

Dalvik堆和逻辑堆得大小(heapSize)并不吻合,这意味着Android并没有使用碎片处理去关闭空闲区域。Android只有当堆末端有没用的空间时才能收缩逻辑堆大小(heapsize),但系统仍然可以减少堆所占用的物理内存大小。在垃圾回收之后,Dalvik会遍历heap并找出不使用的pages,然后使用madvise把那些pages返回给kernal。因此,成对的allocations与deallocations大块的数据可以使得物理内存能够被正常的回收。然而,回收碎片化的内存则会使得效率低下很多,因为那些碎片化的分配页面也许会被其他地方所共享到。

4. 限制应用的内存

为了维持多任务的功能环境,Android为每一个app都设置了一个硬性的heap size限制。确切的heapsize限制随着设备可用的RAM大小而有差异,如果app的heapSize已经达到了最大限制而尝试分配更多的内存就会引起OutOfMemoryError

如果想要查询当前设备的heap size限制大小是多少,然后决定cache的大小。可以通过 getMemoryClass()来查询。这个方法会返回一个整数,表明你的app heap size限制是多少。
也可以通过手机目录system>build.prop文件查看

5、切换应用

当用户切换两个应用时,Android会把那些不是前台的应用()放在 least-recently used (LRU)cache里。比如说当用户第一次启动app时,系统就会为他创建一个进程,但是当用户退出应用是,之前创建的进程并没有退出,系统将进程缓存起来,当用户再返回到应用时,系统就会重用这个进程,这样会让应用切换的更快。

如果你的应用有一个被缓存的进程,虽然它没有被使用,但它仍被保留在内存中,这会影响系统的整体性能。当系统开始进入低内存状态时,它会杀掉Lrucache中最近使用最低的进程,系统也会把进程所占用的内存进行释放。

2、应用如何管理内存

一些Android特性、Java类和代码结构倾向于使用更多的内存。我们可以通过提高代码的效率来减少应用程序对内存的使用

  • 减少Service的使用
    当我们需要启动一个Service进行后来操作时,只有当需要服务执行操作时再启动服务,当服务执行完毕时要及时销毁服务,否在我们在不经意间就会造成内存泄漏。当一个Service已经不需要了却没有销毁是最糟糕的一个内存管理错误。让一个不需要运行的服务运行是最糟糕的一种内存管理错误。

当我们启动一个服务时,系统会倾向于一直保持服务所在的Process,这使得Service所在的Process非常浪费,因为系统没法把Service里所占用的没有用的RAM给其他的Process,这减少了系统能够缓存的进程的数量,使应用间的切换更加低效。它甚至会导致系统内存不稳定,不能够维持正在运行的服务。

一般情况下应该尽量少使用持久性的Service,因为它一直需要占用内存。谷歌推荐我们使用 JobSchedulerIntentService(当处理完Intent后会自动停止)。

  • 使用高效的数据容器
    编程语言提供的一些类在移动设备上用效率并不是很高,例如使用HashMap就会是内存效率很低,因为它每一个映射都需要一个单独的实例。
    Android框架包括几个高效的数据容器,包括 SparseArray, SparseBooleanArray
    LongSparseArray.
    例如,SparseArray类更有效,因为他们避免系统的需要而去对key和value(有时会创建两个)进行autobox(自动装箱)。

  • 注意代码抽象
    开发人员通常使用抽象仅仅作为一个良好的编程实践,因为抽象可以提高代码的灵活性和维护。然而,抽象代价巨大:通常需要大量更多的需要执行的代码,需要更多的时间和更多的RAM代码映射到内存中。如果你的抽象类不提供一个重要的作用,你应该避免他们。

  • 使用nano protobufs序列化数据
    Protocol buffers是由谷歌设计用于序列化结构化数据的语言中立的,与平台无关的,可扩展的机制,和 XML类似,但是更小,更快,更简单。如果你决定让你的代码使用nano protobufs ,你应该一直使用nano protobufs在你的客户端代码里。常规protobufs生成非常冗长的代码,这可能会导致应用产生多种问题比如说增加了内存的使用,增加APK大小,执行慢。

  • 避免内存流失
    正如前面所说的,垃圾回收机制并不影响app的性能。然而许多耗时很短的垃圾回收events能够迅速吃掉你的帧时间。系统花费在垃圾回收上的时间越多,它去做其它事情的时间就越少,比如渲染或者音频流。

    通常,内存流失会引起大量的垃圾回收事件,在实践中,内存流失描述了在指定时间内分配的临时对象的数量。

    例如,你可能会内分配多个临时对象在一个for循环里,或者在onDraw方法里创建Paint或者是Bitmap。这两种情况,都是快速的产生了大量的对象,这些都能很快消耗内存,迫使出现一个垃圾回收事件。

    所以我们需要找到代码流失比较高的地方,这样才能解决问题。推荐使用 Analyze your RAM usage工具找内存流失的地方。

  • ** 移除掉内存占用比较多的资源和库**
    我们的代码里可能会有一些吞噬内存的资源和库却不让我们知道。Apk的整体规模包括第三方的库和或者嵌入的资源会影响app内存的占用。你可以从你的代码中删除任何多余的,不必要的或臃肿的组件来提高应用程序的内存消耗。

  • ** 减少Apk的大小**
    减少APk的整体规模都能够显著的降低app的内存占用。Bitmap的大小,资源,帧动画和第三方库都会影响apk的大小。AndroidStudio和AndroidSDK提供了多种工具来帮助您降低资源和外部依赖的大小。详情可以查看Reduce APK Size.

  • 谨慎使用依赖注入框架
    依赖注入框架如Guice或RoboGuice可以简化你写的代码,并提供一个自适应环境,这是测试和其他配置更改非常有用。然而,依赖框架并不总是为移动设备优化。

    例如,这些框架往往通过扫描你的代码注解初始化过程。这可需要大量代码不必要地映射到RAM中。系统分配这些映射到clean区让Android可以删除它们,这些只有当这些映射存在很长一段时间后才会发生。。。
    如果我们需要注解框架,可以使用 Dagger框架,Dagger没有使用反射扫描应用的代码。Dagger的严格执行意味着它可以在Android应用中使用,而不增加不必要的内存使用情况。

  • ** 谨慎使用扩展类库**
    扩展类库的代码往往不是为移动环境而编写的,当被用在移动客户端上工作时可能比较低效。当您决定使用一个外部库,您可能需要为移动设备优化该库。计划这项工作的前期,并决定在所有使用它之前分析代码大小和内存占用方面的库。

原文链接:
https://developer.android.com/topic/performance/memory.html
https://developer.android.com/topic/performance/memory-overview.html

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

推荐阅读更多精彩内容