android内存管理

内存管理机制概述

Android 的内存管理机制可以简单概括为:系统没有为内存提供交换区,它使用 paging (页式调度)与 memory-mapping(mmapping) 来管理内存。

为什么没有提供交换区?

在linux里面,当物理内存不够用了,而又有新的程序请求分配内存,那么linux就会选择将其他程序暂时不用的数据交换到物理磁盘上(swap out),等程序要用的时候再读进来(swap in)。这样做的坏处显而易见,swap in/swap out这里的代价比较大,相比数据一直放在内存里面,多了读磁盘的操作,而磁盘IO代价太大

The Android Runtime (ART) and Dalvik virtual machine use paging and memory-mapping (mmapping) to manage memory. This means that any memory an app modifies—whether by allocating new objects or touching mmapped pages—remains resident in RAM and cannot be paged out. The only way to release memory from an app is to release object references that the app holds, making the memory available to the garbage collector. That is with one exception: any files mmapped in without modification, such as code, can be paged out of RAM if the system wants to use that memory elsewhere.

这意味着无论是分配一个新的对象或者touching mmapped pages , app的内存发生变化之后都会停留在RAM中,不会被paged out (换页到硬盘)。唯一释放内存的方法就是释放app持有的对象,让内存可以被GC(垃圾回收)。

有一点是例外的:如果系统想在其他地方使用该内存(切换app ,内存不足),同时采用mmapping映射到内存的代码文件(.odex)、资源文件(zipalign后的resource)没有发生改变,这部分内存就会被paged out 。

垃圾回收GC (Garbage collection)

Android Runtime (ART) and Dalvik virtual machine

Dalvik: Dalvik是Google公司自己设计用于Android平台的Java虚拟机它可以支持已转换为 .dex(即Dalvik Executable)格式的Java应用程序的运行,.dex格式是专为Dalvik设计的一种压缩格式,适合内存和处理器速度有限的系统。执行的是字节码,它是依靠Just-In-Time (JIT)机制去解释字节码

ART:即Android Runtime,google为了替代Dalvik专门为Android研发的。Android KK为开发者推出,L版本正式上线。比替代品更高效省电,执行的是本地机器码,依靠Ahead-Of-Time (AOT)机制

Android Runtime内存堆划分

what’s gc

A managed memory environment, like the ART or Dalvik virtual machine, keeps track of each memory allocation. Once it determines that a piece of memory is no longer being used by the program, it frees it back to the heap, without any intervention from the programmer. The mechanism for reclaiming unused memory within a managed memory environment is known as garbage collection. Garbage collection has two goals: find data objects in a program that cannot be accessed in the future; and reclaim the resources used by those objects.

ART 或者 DVM 会一直跟踪每一块内存的分配, 一旦发现某一块内存没有被使用,就会自动返还给内存堆。在管理内存环境中回收没有用内存堆机制,称之为垃圾回收(GC )。
GC有两个目的:
1. 发现程序中在未来不会被访问到的数据对象
2. 回收那些被对象使用的资源

generation

Android’s memory heap is a generational one, meaning that there are different buckets of allocations that it tracks, based on the expected life and size of an object being allocated. For example, recently allocated objects belong in the Young generation. When an object stays active long enough, it can be promoted to an older generation, followed by a permanent generation.

Android的内存堆是一块大的内存空间,基于可预期的存活周期和分配的大小,堆被划分成两个不同的区域:新生代 ( Young )、老年代 ( Old )。新生代 ( Young ) 又被划分为三个区域:Eden、From Survivor、To Survivor。

新生代:Young Generation,主要用来存放新生的对象。
老年代:Old Generation或者称作Tenured Generation,主要存放应用程序生命周期长的内存对象。

/永久代:在JDK1.6版本之后,永久代就要被取消掉了,只留下年轻代和老年代。(方法区,不属于java堆,另一个别名为“非堆Non-Heap”但是一般查看PrintGCDetails都会带上PermGen区)是指内存的永久保存区域,主要存放Class和Meta的信息,Class在被 Load的时候被放入PermGen space区域. 它和和存放Instance的Heap区域不同,GC(Garbage Collection)不会在主程序运行期对PermGen space进行清理,所以如果你的应用会加载很多Class的话,就很可能出现PermGen space错误。/

堆大小 = 新生代 + 老年代。其中,堆的大小可以通过参数 –Xms、-Xmx 来指定。
默认的,新生代 ( Young ) 与老年代 ( Old ) 的比例的值为 1:2 ( 该值可以通过参数 –XX:NewRatio 来指定 ),即:新生代 ( Young ) = 1/3 的堆空间大小。老年代 ( Old ) = 2/3 的堆空间大小。其中,新生代 ( Young ) 被细分为 Eden 和 两个 Survivor 区域,这两个 Survivor 区域分别被命名为 from 和 to,以示区分。
默认的,Edem : from : to = 8 : 1 : 1 ( 可以通过参数 –XX:SurvivorRatio 来设定 ),即: Eden = 8/10 的新生代空间大小,from = to = 1/10 的新生代空间大小。
JVM 每次只会使用 Eden 和其中的一块 Survivor 区域来为对象服务,所以无论什么时候,总是有一块 Survivor 区域是空闲着的。
因此,新生代实际可用的内存空间为 9/10 ( 即90% )的新生代空间。

Generation

性能影响

Each heap generation has its own dedicated upper limit on the amount of memory that objects there can occupy. Any time a generation starts to fill up, the system executes a garbage collection event in an attempt to free up memory. The duration of the garbage collection depends on which generation of objects it's collecting and how many active objects are in each generation.

堆的每一个区域的对象占据的内存都有上限 ,只要某一代开始占满,系统就会执行一个GC事件尝试去释放内存。触发暂停的时间依赖于在某代和有多少可回收的对象

Even though garbage collection can be quite fast, it can still affect your app's performance. You don’t generally control when a garbage collection event occurs from within your code. The system has a running set of criteria for determining when to perform garbage collection. When the criteria are satisfied, the system stops executing the process and begins garbage collection. If garbage collection occurs in the middle of an intensive processing loop like an animation or during music playback, it can increase processing time. This increase can potentially push code execution in your app past the recommended 16ms threshold for efficient and smooth frame rendering.

即使GC是相当快的,但仍然会影响到app的性能。你不能用代码来直接控制gc事件的发生。系统有一套执行GC的标准,符合条件后,系统会停止执行中的进程,开始GC。如果GC发生在一个循环处理的过程当中(动画或者音乐回放),会增加其处理的时间。这个增加的时间可能会影响app的帧数,导致达不到推荐的16ms閾值。(Android 屏幕 每秒刷新60hz,相当于16ms渲染,超过16ms,将在下次16ms中刷新,故产生卡顿。)

Additionally, your code flow may perform kinds of work that force garbage collection events to occur more often or make them last longer-than-normal. For example, if you allocate multiple objects in the innermost part of a for-loop during each frame of an alpha blending animation, you might pollute your memory heap with a lot of objects. In that circumstance, the garbage collector executes multiple garbage collection events and can degrade the performance of your app.

你的代码流可能会让GC事件频繁发生,执行时间变长。例如,在每一帧的混合动画中,在for循环的内部处理多个对象,这样可能就会污染到内存堆。在这种场景,GC会执行多次,这样就会降低app的性能。

For more general information about garbage collection, see Garbage collection.

Sharing Memory(内存共享)

  • 每一个app进程都是由Zygote fork 出来的,当系统启动时zygote开启,加载framework code和resource(如activity theme) 。 然后系统会fork zygote 开启一个新的app进程,在此进程中加载运行app的代码。这个模式允许大部分分配到framework code 跟 resource的RAM pages 能够跨进程共享。

  • 大部分到静态数据是采用mmapped 到进程内,mmapped允许进程间共享数据,也允许paged out 出RAM 。
    静态数据包括:
    1、 Dalvik code(将直接mmapping的放置在 预连接的.ode文件)
    2、app resources(设计成结构后能mmapp 的resource table,apk中的对齐后的zip实体,也是zipalign app)
    3、传统的项目元素(如.so文件中的native code)

  • 在很多地方,Android通过显式的分配共享内存区域(ashmem或gralloc) 来跨进程共享 相同的动态RAM。例如:window surfaces在app和screen compositor之间共享内存,cursor buffer在content provider和client之间共享内存。

分配与回收 (Allocating and Reclaiming App Memory)

app进程的Dalvik heap被限制在 single virtual memory range(单独一块的虚拟内存范围内),heap size可以增加,但是系统定义了上限。

heap使用的虚拟内存 不等于 使用的物理内存大小。当监测 APP的heap时,Android统计一个数值被称为Proportional Set Size(PSS),PSS包含与其他进程共享的 dirty and clean pages,但共享内存只统计 一个app所占RAM比例的大小(如:3个app共享15个pages,则PSS中共享内存为5个pages)。系统认为PSS是物理内存的footprint(印记,也就是消耗了,像碳排放印记)。
Dalvik堆栈不计算堆栈的逻辑大小,也就是Android不会对heap进行碎片整理来释放堆栈的空间。android只能通过删除堆栈的尾部不用的空间来减少堆栈的大小,但这并不是说物理内存不能被压缩(不能从堆栈中间删除数据)。

GC回收器会浏览堆栈,并把堆栈中不用的页空间回收到内核通过madvise这个函数(这个函数可以对映射的内存提出使用建议,从而提高内存)。所以,成对分配与回收大块的数据可以全部(几乎全部)回收物理内存。然而用同样的方式回收小块数据的效率就低了。因为小块数据可能还被其他的使用,且引用还没有释放。

限制App内存(Restricting App Memory)

为了维护多任务的运行,android 为每个应用设置了一个硬性的内存堆栈限制,一旦应用使用的内存超过这个值就会报OUtOfMemoryError的错误。

一些情况下,你的应用可能需要知道当前运行系统环境对堆栈内存的限制大小是多少,从而做出合理的操作,不如缓存多少数据,使用高清图片还是普通图片。可以通过getMemoryClass()的方式获取这个限制的大小,详见Check how much memory you should use

app切换( Switching apps)

当用户切换应用的时候,别切换到后端的应用使用的内存并没有被切换或者删除,进程会被缓存到LRU缓存中。比如用户首先启动了一个应用,系统会创建一个进程,当用户离开这个应用,这个进程并没有退出,而是别缓存到LRU缓存中,所以当用户再次返回到这个应用的时候,能更快速更高效的启动,从而应用切换更快。

如果你的应用被缓存到LRU中,还在不断的申请内存或不释放占用的(不需要的)内存,这会影响到系统的性能,所以,当系统进行到内存低可用的时候,会从LRU缓存中最早一个被缓存的程序开始移除(释放进程暂用的资源),同时也会考虑应用所暂用的内存的大小。为了能让你的应用尽可能长的在LRU中缓存,可以更加以下建议释放引用的资源。

更多关于系统入会缓存应用到LRU已经如何移除应用,参考Processes and Threads

Android内存管理机制详解 - CSDN博客
Android 开发进阶之『清除应用中的内存泄漏』 - 简书
谈谈Android的内存管理机制
Overview of Android Memory Management | Android Developers
Android 性能优化 内存优化 How Android Managers Memory - baiiu - CSDN博客
Java堆内存 - ImportNew
http://blog.csdn.net/zhjali123/article/details/75270571
http://blog.csdn.net/z8711042/article/details/18665933

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

推荐阅读更多精彩内容

  • 一.操作系统相关基础知识 1.物理内存、虚拟内存、逻辑地址与交换空间 物理内存(RAM):加载到内存地址寄存器中的...
    Geeks_Liu阅读 9,015评论 6 30
  • Random Access Memory(RAM)在任何软件开发环境中都是非常重要的资源,但在物理内存通常很有限的...
    吾若成疯阅读 1,202评论 0 10
  • 1.Android 内存基础 所有的内存都是基于物理内存的,即移动设备上的RAM。当启动一个Android程序时,...
    KaelQ阅读 2,122评论 3 22
  • 经过21天的至简心法的修习体验得到如下几多收获: 1.我开始懂得聚焦的观察自己的内心,在固定的时间,固定的地点,程...
    般若明阅读 844评论 0 1
  • 繁星满天,也许只有乡村的夜空才如此。 路过位于小学旧址的施工工地,一小轿车打着远光扬长而去,顿时尘土飞扬。不...
    熬大骨头汤阅读 285评论 0 0