安卓 9.0php 内存回收机制制是否做了优化

[Android 性能优化系列]内存之终极篇--降低你的内存消耗
大家如果喜欢我的博客,请关注一下我的微博,请点击这里(http://weibo.com/kifile),谢谢
转载请标明出处(http://blog.csdn.net/kifile),再次感谢
原文地址:http://developer.android.com/training/articles/memory.html
在接下来的一段时间里,我会每天翻译一部分关于性能提升的官方文档给大家
建议大家在看本文之前先去我的博客看看
性能优化系列]内存之基础篇--Android如何管理内存
性能优化系列]内存之提升篇--应用应该如何管理内存
这一篇文章是Android官网内存部分的最后一节,看完了这个内存文档之后,发现很多东西自己以前从来没有差觉过,比如对象和类所消耗的内存空间,依赖库的使用,多进程的使用等等。希望大家能够好好看看,多理解一下Android的内存机制,如果觉得我翻译的不够到位,自己希望看看官网的原文,可以点击上面的链接去查看。
下面是本次的正文:
################
拒绝在 Bitmap 上浪费你的内存
当你加载一张 Bitmap 的时候,你可以选择只在你的确需要在屏幕上显示的时候才将它加载到内存里,或者通过缩放原图的尺寸来减小内存占用。请记住随着 Bitmap 尺寸的增长,图片所消耗的内存会成平方量级的增长,因为 Bitmap 的 x 轴和 y 轴都在增长。
注意:在 Android2.3及以下的平台,Bitmap 对象在你应用堆中的大小总是一样的(因为他实际的内存被单独存放在了本地内存中)。这会使得我们分析 Bitmap 消耗的内存资源变得非常困难,因为大多数分析工具只收集你应用的 Dalvik 堆信息。但是,从 Android3.0开始,为了提升垃圾收集和调试的能力,Bitmap 的像素数据被放在了你的 Dalvik 堆里。因此如果你的应用使用了 Bitmap 并且你在老设备上无法发现应用的内存消耗问题,那么请你在
Android3.0或者更高的机型上调试他。
对于更多使用 Bitmap 的帮助,你可以去查看Manage Bitmap Memory(我强烈建议可以去看看,对于降低Bitmap的内存占用很有帮助)
使用优化后的数据容器
请使用 Andorid 框架中优化过的数据容器,例如 SparseArray,SparseBooleanArray 和 LongSparseArray。类似于 HashMap 这一类的容器的效率不是很高,因为在每个 Map 中对于每一次的存放数据,他都需要独立一个单独的 Entry 对象进行传芳。而 SparseArray 由于禁止自动封装键值对,因此他更加有效率。并且你不需要担心丢失掉原有信息
小心内存花销
请对你正在使用的语言和依赖包拥有一定的了解,并且在你设计应用的整个阶段,都不要忽视它。通常大多数看起来无害的东西都可能让你花费大量的内存,比如说一下的几个:
1.枚举与静态常量相比,通常会消耗两倍的内存资源,因此你应该尽量避免在 Android 中使用枚举类型
2. 中的每一个类(包括匿名内部类),都会消耗大约500比特内存
3.每一个类对象都会消耗12-16比特内存
4.把单个 Entry 放入 HashMap 需要多消耗32比特的内存(原因请参看上一小节,使用优化后的数据容器)
虽然这里的消耗看起来比较少,但是他们累计起来就很大了,应用中设计那些重量级的类就很可能承受这些内存花销。这会使你的堆分析变得困难起来,你很难发现你的问题其实是因为很多小的对象正在占用你的内存。
小心抽象代码
通常,开发者都会将抽象作为一种好的习惯,因为抽象可以提升代码的灵活性和可维护性。但是,抽象方法可能带来很多的额外花费,例如当他们执行的时候,他们拥有大量的代码,并且他们会被多次映射到内存中占用更多的内存,因此如果抽象的效果不是很好,那么最好放弃他
对于序列化数据使用 Protobufs
Protocol buffers 是谷歌一种跨语言,跨平台的结构化序列数据,相比于 XML,他更小,更快,更简单。如果你决定让你的数据使用 Protobufs,你应用总是在你的客户端使用纳米级的Protobufs。规则的Protobufs会产生极其冗余的代码,这可能会导致应用产生各种问题:增加内存使用,APK包体增加,执行效率变慢,打破Dex的符号限制。
更多的信息,请参看protobuf readme
避免依赖注入框架
使用类似于 Guice 和 RoboGuice 的依赖注射框架,或许会使你的代码变得更加漂亮,因为他们能够减少你需要写的代码,并且为测试或者在其他条件改变的情况下,提供一种自适应的环境。但是,这些框架在初始化的时候会因为注释而消耗大量的工作在扫描你的代码上,这会让你的代码在进行内存映射的时候花费更多的资源。虽然这些内存能够被 Android 进行回收,但是等待整个分页被释放需要很长一段时间。
小心使用外部依赖包
很多依赖包都不是专门为了移动环境或者移动客户端写的。如果你决定使用一个外部依赖包,你应该提前明白你需要为了将它移植到移动端而消耗花费大量的时间和工作量。请在使用外部依赖包得时候提前分析他的代码和内存占用
即使依赖包是为了 Android 而设计的,但是这也有潜伏的危险,因为每一个包都做着不同的工作。例如,有一个依赖包使用纳米级的 protobufs 但是别的包使用微米级的 protobufs.那么现在在你的应用中就有两套 protobuf 的标准了。这会在你记录数据,分析数据,加载图像,缓存,或者其他任何可能的情况下发生你不希望发生的事情。ProGuard 无法在这里帮助你,因为他们都是你所依赖包的底层实现,。当你使用从别的依赖包(他可能继承了很多的依赖包)里继承的
Activity 时,这个问题变得尤其严重,当你使用反射以及干别的事情的时候
请注意不要落入一个依赖包的陷阱,你不希望引入一大片你根本不会使用到的代码。如果你无法找到一种已经实现的逻辑来完全满足你的需求,那么你尽量创建一个自己的实现方式。
优化整体性能
很多关于优化应用的整体性能的信息被放到了 Best
Practices for Performance,这里的很多文章介绍了如何优化 CPU 性能,但是很多小提示能够帮助你优化你的应用的内存使用,比如说通过减少你ui 的布局元素。
你应该读一下优化你的 ui,并使用布局调试工具来进行优化,另外可以通过 lint 工具来进行优化
使用 ProGuard 来剔除你不用的代码
ProGuard 工具能够通过移除不用的代码以及对类,方法和标量进行无意义的重命名来起到回收,优化和混淆代码的作用,使用 ProGuard 能够使你的代码变得更紧凑,而且减低内存消耗
使用 Zipalign 来优化你的 Apk
如果你对你生成的 APK 文件做了后期优化,那么你必须要使用 Zipalign 来让他对齐字节。不这样做可能会导致你的应用因为从 APK 里的资源不能被很好的映射到内存里而消耗更多的内存。
注意:现在 Google 市场不接受没有通过 Zipalign 处理过的 APK 文件
分析你的内存使用
一旦你已经拥有一个稳定版本的应用,那么就从他的整个生命周期开始分析你应用的使用内存。更多关于你应用的内存分析,请查看Investigating
Your RAM Usage.
使用多进程
你应该在恰当的时候将你的应用分布到多个进程中,这能够帮助你管理你的应用内存。你应该被小心的使用这个技术,而且绝大部分应用都没有必要在多线程中运行。如果错误的使用,反而可能导致你的应用消耗更多的内存,而不是减少。
举个例而言,你可以在构建一个音乐播放器的时候使用多进程,因为他的后台服务会持续很长一段时间,当整个应用运行在一个进程中,他的 activity 界面上的那些资源在播放音乐的时候还会被保存,即使这个时候用户已经去到了另外一个应用,但是你的服务还在播放音乐。这样的一个应用应该独立的使用两个进程,一个用于 ui 线程,另一个用于执行后台服务。
你可以通过在 manifest 文件中为组件定义 android:process 属性来为他们区分所属的进程。例如,你可以让你的某个服务运行在独立于主线程的进程中,通过定义一个进程明教“backgroud”,这个名字可以随便定义
你的进程的名称应该以一个逗号开始,以确保这个进程是你应用私有的
在你决定创建一个新进程之前,你需要理解内存的消耗情况。为了说明每个进程的重要性,我们来看看一个空进程,即使他什么事情也不做,他也会消耗1.4M 的空间,就像下面的内存信息里显示的那样:
adb shell dumpsys meminfo com.example.android.apis:empty
** MEMINFO in pid 10172 [com.example.android.apis:empty] **
Shared Private
Shared Private
Native Heap
Dalvik Heap
Dalvik Other
Other mmap
注意,关于如何这些输出的信息,可以查看 Investigating
Your RAM Usage.,这里主要的内容是 Private Dirty 和 Private Clean 这两块,他们分别显示了这个进程使用了大约1.4M 的未分页内存(分别分布在 Dalvik 堆,本地分配空间,类库加载),此外还有大约150k
的内存被映射到了内存执行。
这个空进程的内存输出情况是相当清楚的。当你在进程中进行操作时,进程的内存还会继续增大。举个例子,这里是一个只在 activity 上显示了一些文本的进程的内存数据:
** MEMINFO in pid 10226 [com.example.android.helloactivity] **
Shared Private
Shared Private
Native Heap
Dalvik Heap
Dalvik Other
Other mmap
这个进程现在的大小差不多有4M,但是他真的只在 ui 界面上显示了一些文本。通过这个,我们可以得出结论,如果你打算将你的应用分割到多进程中,那么只能让一个进程响应 ui,其他进程应该避免响应 ui,否则会极大的提升内存占用,尤其是当你加载了 bimap 或者其他一些资源的时候。这会导致 ui 绘制完成之后,我们也很难甚至于说不可能降低内存消耗。
除此之外,当运行超过一个进程的时候,你应该尽可能让你的代码保持简洁。因为那些不必要的内存。例如说,如果你使用了枚举(我们在之前讨论过枚举的内存消耗大约是静态常量的两倍),那么所有的进程中 RAM 都需要创建并且初始化这些常量,而且你在局部区域或者适配器中使用过的抽象方法也会被反复复制。
多进程的另一个核心是两者之间的依赖关系,比如说,如果你的应用中有一个运行在默认进程上的内容提供者,那么在后台进程中的代码如果想要访问这个内容提供者,就需要在 ui 线程中。如果你的目标是避免在繁重的 ui 线程中执行操作,那么你就不应该依赖处于 ui 线程中的内容提供者或者服务。
##########我是分隔符#######################
Ok,关于Android的内存,就翻译到这里了,之后我会将更多的官网文章翻译过来,希望大家 喜欢,如果点赞就更好了Java&Android的垃圾回收机制 - 简书
Java&Android的垃圾回收机制
Java的 jvm 和 Android 的dvm的小笔记。
Java 垃圾回收
JVM中垃圾回收有多种机制和内存管理密不可分,它是基于这样的假设:大部分对象是小的会被快速创建并回收,少部分对象很大并且会存活很长时间,按照对象的生命长度JVM划分了三个代,并采用了不同的垃圾回收算法。
年轻代(Young generation) 分Eden 和 Survivor(From,To)
成熟代(Tenured generation)
永久带(permanent generation)
年轻带采用 “Copy and Sweep” 的方式管理内存,对象不是直接被回收而是活着的对象被Copy到活的区域,死的对象留在了死的区域。第一轮追踪完之后活着的对象会被Copy到Survivor区域。而”死区“会被清零(Sweep),留在这里的对象也被一并清空了。
如果一个对象多次被Copy的话,会进入到成熟代,这个地方采用新的回收算法"Mark and Sweep", 一轮追踪之后活着的对象会被标记,随后JVM扫描整个堆,完成剩余对象(没有被标记的对象)的回收。
永久带一般存放类的信息,不会被回收。
年轻带的处理方式每次Copy可以重新对齐和分配内存,可以减少内存碎片,但是这个过程会暂停JVM的执行,但是这个过程会很快因为大部分对象会被干掉只有少部分对象会被移动。
成熟带的处理,是串行+并行的方式进行的
第一次标记串行 找到Root节点,因为Root很少所以很快
第二次并行标记余下的节点
重新标记 在并行标记的时候可能会有对象发生变化,所以需要重新标记一遍,这个时候会用一个特殊的数据结构来很快找到变化的对象
并行回收 回收的工作量很大,所以采用并行的方式
所以整个回收的过程依然很快,但是这种方式会产生内存碎片.
Android内存结构:
有待继续研究
Android内存结构分两部分
JavaObject 内存 ,DVM占用内存
Native内存(用JNI分配),Linux系统内存
Android中的DVM内存是可变的,也就是App刚刚启动的时候可能才占用8M内存,但是用了一会儿(中间加载了几张图片),内存就会自动涨到9M。如果App在运行的时候继续申请内存就会达到真正的App内存上限。在Android最开始的时候,建议的最低配是16M,至今为止大部分手机生产上默认的配置都会远大于这个值(一般50M~几百M都有)。
图片的内存分配 在3.0之前的时候,图片的二进制数据是在Native上分配的,所以要手动调用recycle()方法手动清除,但是在之后(包括3.0)回到了JavaObject,所以会被GC管理。
DVM内存管理 在2.3 版本之前内存回收是停止JVM(stop-world)对整个堆进行回收,之后可以并行并且局部回收(在Logcat中可以看到打印的Log)。
目前神秘项目开发中,代号:korok!!
1.什么是垃圾回收?
垃圾回收(Garbage Collection)是Java虚拟机(JVM)垃圾回收器提供的一种用于在空闲时间不定时回收无任何对象引用的对象占据的内存空间的一种机制。
注意:垃圾回收回收的是无任何引用的对象占据的内存空间而不是对象本身。换言之,垃...
用两张图告诉你,为什么你的 App 会卡顿? - Android - 掘金Cover 有什么料? 从这篇文章中你能获得这些料: 知道setContentView()之后发生了什么? ... Android 获取 View 宽高的常用正确方式,避免为零 - 掘金相信有很多朋友...
用两张图告诉你,为什么你的 App 会卡顿? - Android - 掘金 Cover 有什么料? 从这篇文章中你能获得这些料: 知道setContentView()之后发生了什么? ... Android 获取 View 宽高的常用正确方式,避免为零 - 掘金 相信有很多...
垃圾回收算法具体实现 翻译原文 =& plumbr Java GC handbook 前文参见: Java垃圾回收手册(一):初识垃圾回收Java垃圾回收手册(二):Java中的垃圾回收Java垃圾回收手册(三):垃圾回收算法基础 在熟悉GC算法背后的核心概念之后,我们来看...
Java 基础思维导图,让 Java 不再难懂 - 工具资源 - 掘金思维导图的好处 最近看了一些文章的思维导图,发现思维导图真是个强大的工具。了解了思维导图的作用之后,觉得把它运用到java上应该是个不错的想法,这样回顾知识点的时候一目了然,快速知道自己的短板。 思维导图...
我把头交给了你 你温柔的挠着它 还问我痒不痒 贱人 我的头皮都在回应你 只想你再用力 从头发抓进我的身体
肉苁蓉 / Ròu Cōnɡ Rónɡ HERBA CISTANCHES 上一期:坚持每天最少记一味中药—半枝莲 | 第62天 性能特点: 本品甘咸而温,质地柔润,药力较缓,不甚燥热。入肾经,能补肾阳、益精血;入大肠经,能润肠燥、缓通便。 功效: 补肾阳,益精血,润肠通便。...
梧桐细雨暮时光,孤灯长袖裳。西风卷起落花飏。池塘两岸苍。 门前柳,影边窗,几分凄婉尝。年年岁岁晚秋凉,离人见旧觞。
(词林正韵) 更多诗词:中国经典古诗词
亲爱爸爸妈妈: 你们辛苦了 昨天晚上打电话我们交谈的很久,几乎我们第一次聊这么长时间,应该让父母担心了。在这里说一下对不起,可能是因为从小我就没有人家里人放心过,所以这次仓促的决定,也让家人为难。 其实我知道不够我多少岁,长多高,在家里面我永远都是孩子。但是你们也要知道现在...如果我们想要进行内存优化的工作,还是需要了解一下,但这一块的知识属于纯理论的,有可能看起来会有点枯燥,我尽量把这一篇的内容按照一定的逻辑来走一遍。首先,我们为什么要学习垃圾回收的机制,我大概归纳了一下几点:
1.方便我们理解什么样的对象,什么时候,会被系统回收掉
2.有助于我们后面的内存优化
3.了解这一块的知识也能提升自己的知识广度,和同事一起装逼的时候有话题
4.如果有面试的需求的话,了解这一块,也能从容面对考官,对于内存回收能够说出个一二
好了,废话不多说了,我大概按以下这个逻辑来一个一个讲述:
1.什么是垃圾回收(GC)
2.垃圾回收机制对于我们来说有什么好处?又有什么缺点?
3.垃圾回收它是如何工作的?
1.什么是垃圾回收(GC)
垃圾回收或GC(Garbage Collection),是一种自动的存储管理机制,它是Java语言的一大特性,方便了我们这些程序员编码,把内存释放工作的压力都转让到了系统,故而是以消耗系统性能为代价的。C++编码的时候,我们 需要自己实现析构函数来进行内存释放,很麻烦,而且非常容易遗漏而最终导致程序崩掉。所以Java语言就引入了自动内存管理的机制,也就是垃圾回收机制,针对的主要的内存的堆区域,关于内存的分配机制,请查看我的上一篇
2.垃圾回收机制对于我们来说有什么好处?又会带来哪些坑?
以下我列举一下系统自动垃圾回收给我们带来的一些好处:
1.让作为程序员的我们专注于逻辑实现,提高了我们的编码效率
2.能够保护程序的完整性,垃圾回收是Java语言的安全策略的一个重要部分
但是随之的,也会到来一些缺点:
1.系统层面负责垃圾回收,明显会加大系统资源的开销,从而影响程序的性能
2.垃圾回收机制也存在不完备性,并不能百分百保证回收所有的垃圾内存
3.垃圾回收它是如何工作的?
其实,GC是主要的一个流程是:先根据一定的算法判定某个对象是否存活,然后把判定是垃圾的对象进行回收。详细点说的话,GC的工作流程分以下几个步骤:
1.可回收对象的判定
2.通过某些算法回收垃圾内存
下面一个一个进行讲述
1.可回收对象的判定
我们的GC需要把某个对象回收掉,肯定是需要判断它到底是不是垃圾,是不是需要被回收,因此,就需要对每一个对象进行可回收判定。
目前,市面上存在有两种算法来判定一个对象是否是垃圾
1.引用计数算法
这种算法的工作原理是:
1.首先给每一个对象都添加一个引用计数器
2.当程序的某一个地方引用了此对象,这个计数器的值就加1
3.当引用失效的时候(例如超过了作用域),这个计数器就减1
4.当某一个对象的计数器的值是0的时候,则判定这个对象不可能被使用
这种算法对于系统来说比较简单,高效,垃圾回收器运行较快,不需要长时间中断我们的程序的执行,但是缺点是很难处理循环引用,这就导致相互引用的对象都无法被回收:
我记得OC(Objective-C)中的垃圾判定就是用的引用计数方法,引用了一个第三方变量来打破这个平衡,但OC也没有很好的解决这个问题,而是更多的依靠我们这些开发者来处理。
2.GC Root可达性分析算法
这个算法的工作原理是:
1.以称作“GC Root”的对象作为起点向下搜索
2.每走过一个对象,就生成一条引用链
3.从根开始到搜索完,生成了一棵引用树,那些到GC Root不可达的对象就是可以回收的
这个方法明显就解决了循环引用的问题,不过这个算法还是稍微有点复杂的,以下是GC Root可达性算法的一个图解:
我们程序中能够被用来当做GC Root对象的有:
1.虚拟机栈(栈帧中的本地变量表)中引用的对象
2.方法区中静态属性引用的对象
3.方法区中常量引用的对象
4.本地方法栈中JNI引用的对象
以下拿一个图来进行引用计数算法与可达性分析算法的比较:
文字说明:
1.若使用引用计数算法判定,但图中的C和D对象存在相互引用,导致计数器不为0,无法回收掉
2.若使用可达性分析算法,C和D对象到GC Roots 不可达,则能够回收
关于对象可回收的判定,我们还需要注意的是,当系统开始启动GC的时候,为了保证引用链的状态不变,就需要停止该进程中所有的程序(Stop The World),我们Android中的现象就是UI卡顿了,但一般这样的卡顿时间是非常短的,当然,要是频繁的产生GC,那就另当别论了。
还有值得注意的是,不可达对象也并非立即就被回收了,还需要经过两次的标记过程后才被会被真正回收:
1.如果对象与GC Root没有连接的引用链,就会被第一次标记,随后判定该对象是否有必要执行finalize()方法
2.如果有必要执行finalize()方法,则这个对象就会被放到F-Queue的队列中,稍后由虚
拟机建立低优先级的Finalizer线程去执行,但并不承诺等待它运行结束(对象类中能够
重写finalize()方法进行自救,但系统最多只能执行一次)
3.如果没必要执行finalize()方法,则第二次标记
2.通过某些算法回收垃圾内存
以上内容讲述了系统如何去判定某一个对象是否是垃圾,是否应该被回收。接着,当判定了某一个对象为垃圾对象后,系统就要开始进行回收了,那么系统的垃圾回收算法以下几种:
1.标记清除算法(Mark-Sweep)
2.复制算法(Copying)
3.标记整理算法(Mark-Compact)
4.分代回收算法
以下进行一一讲述
1.标记清除算法(Mark-Sweep)
顾名思义,这个算法是先进行标记,然后进行清除,也正是这个算法的两个阶段:标记阶段和清除阶段,以下图解:
从图中可以看出:
这个算法的优点是易于理解,容易实现,只需要将特定地址的空间进行处理。
但缺点也比较明显,把整个内存区域弄得非常不完整,形成了很多碎片化的内存,对于
分配大内存的对象时,无法申请足够的空间,从而更多次的触发GC
2.复制算法(Copying)
复制算法,是对标记清除算法而导致内存碎片化的一个解决方案,算法原理如下:
1.如图所示,复制算法将内存平均分成两个区域A (上)和 B(下)
2.将A中的存活的那些对象复制到B区域中
3.然后将A区域的所有对象都清除,这样A区域就是一个完整的内存块了,也就避免了内
存碎片化了
但是这个算法也有明显的缺点,那就是不管A区域或B区域有多少个存活对象,都需要将整块内存分成两个区域,意味着能够真正使用的内存变成了一半。
3.标记整理算法(Mark-Compact)
标记整理算法是对于标记清楚的一个优化,工作原理是:
1.如图所示,第一步也需要进行存活对象的一个标记,这一步与标记清除算法一模一样
2.将存活的对象向一端移动,例如图中是往左上角那一端进行移动
3.然后把另一端的内存进行清理
从图中也可以看出,这个算法也能避免内存碎片化的问题,但是效率确实不怎么样,毕竟相较于复制算法, 多了一步效率同样比较低的标记过程,而与标记清除算法相比,多了一步内存整理(往一端移动)的过程,效率上明显就更低了。
毕竟世界是公平的,任何算法都有两面性,我们开发者只能具体情况具体分析,使用最适合的算法。因此标记整理算法从图中可以看出,这个算法适合存活对象多的,回收对象少的情况。
4.分代回收算法
鉴于以上三种算法都存在自己的缺陷,然后大神们就提出了根据不同对象的不同特性,使用不同的算法进行处理,所以严格来讲并不是一个新的算法,而是属于一种算法整合方案,我们知道:
1.复制算法适用于存活对象少,回收对象多的情况
2.标记整理算法适用于存活对象多,回收对象少的情况
这两种算法刚好互补,因此只要将这两个算法作用于不同特性的对象,就完美了。。
那么我们就应该知道,哪个区域的对象是什么样的特性,根据我的上一篇的内存分配模型,堆内存中的新生代有很多的垃圾需要回收,老年代有很少的垃圾需要回收,那么刚好能够根据这个特点使用不同的算法进行回收,具体使用的方式为:
1.对于新生代区域采用复制算法,因为新生代中每次垃圾回收都要回收大部分对象,那么也就意味着复制次数比较少,因此采用复制算法更高效
2.而老年代区域的特点是每次回收都只能回收很少的对象,一般使用的是标记整理或者标记清除算法
通过以上的方式,使得GC的整个过程达到了最高效的状态。
这里需要注意的是分代回收算法的中的复制算法的使用。
之前说的复制算法是将内存均分为二,但是在分代回收中,并不是这样,而是根据Eden:Survivor A:Survivor B= 8:1:1,具体的过程是(简化版):
1.新创建一个对象,默认是分在Eden区域,当Eden区域内存不够的时候,会触发一次Min
or GC(新生代回收),这次回收将存活的对象放到Survivor A区域,然后新的对象继续放在Eden区域
2.再创建一个新的对象,要是Eden区域又不够了,再次触发Minor GC,这个时候会把Eden区域的存活对象以及
Survivor A区域的存活的对象移动到Survivor B区域,然后清空Eden区域以及Survivor A区域
3.如果继续有新的对象创建,不断触发Minor GC,有些对象就会不断在Survivor A区域以及Survivor B区域
来回移动,但移动次数超过15次后,这些对象就会被移动到老年代区域
4.如果新的对象在Minor GC后还是放不下,就直接放到老年代
5.如果Survivor区域放不下该对象,这直接放到老年代
6.如果老年代也满了,就会触发一次Full GC(major gc)
通过以上的讲述,我们了解了什么是GC,它的优缺点,它是如何工作的,整过过程下来,脑子里也算有了一个GC的概率模型在了。
本文参考了以下博客:
掌握好GC策略和原理,对于我们编码来说能够避免一些不必要的内存泄露,我们使用Java语言进行开发,不要一味的去追求各种牛逼的框架或者酷炫的业务实现,有的时候,还是需要我们沉下心来,好好了解一下底层系统的一些机制,个人觉得还是很有必要的。
作者:进击的欧阳链接:http://www.jianshu.com/p/4b6adee12682來源:简书著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
阅读(...) 评论()Android内存分配回收机制
一 ,Android内存分配回收机制
从宏观角度上来看Android系统可以分为三个层次
1. Application Framework,
2. Dalvik 虚拟机
3. Linux内核。
这三个层次都有各自内存相关工作:
1. Application Framework
Anroid基于进程中运行的组件及其状态规定了默认的五个回收优先级:
Empty process(空进程)Background process(后台进程)Service process(服务进程)Visible process(可见进程) Foreground process(前台进程)系统需要进行内存回收时最先回收空进程,然后是后台进程,以此类推最后才会回收前台进程(一般情况下前台进程就是与用户交互的进程了,如果连前台进程都需要回收那么此时系统几乎不可用了)。
由此也衍生了很多进程保活的方法(提高优先级,互相唤醒,native保活等等),出现了国内各种全家桶,甚至各种杀不死的进程。
Android中由ActivityManagerService 集中管理所有进程的内存资源分配。
2. Linux内核
这里最简单的理解就是ActivityManagerService会对所有进程进行评分(存放在变量adj中),然后再讲这个评分更新到内核,由内核去完成真正的内存回收(lowmemorykiller,
Oom_killer)。这里只是大概的流程,中间过程还是很复杂的,有兴趣的同学可以一起研究,代码在系统源码ActivityManagerService.Java中。
3. Dalvik虚拟机
Android中有Native Heap和Dalvik Heap。Android的Native Heap言理论上可分配的空间取决了硬件RAM,而对于每个进程的Dalvik Heap都是有大小限制的
Android App为什么会OOM呢?其实就是申请的内存超过了Dalvik Heap的最大值。这里也诞生了一些比较”黑科技”的内存优化方案,比如将耗内存的操作放到Native层,或者使用分进程的方式突破每个进程的Dalvik Heap内存限制。
Android Dalvik Heap与原生Java一样,将堆的内存空间分为三个区域,Young Generation,Old Generation, Permanent Generation。
最近分配的对象会存放在Young Generation区域,当这个对象在这个区域停留的时间达到一定程度,它会被移动到Old Generation,最后累积一定时间再移动到Permanent Generation区域。系统会根据内存中不同的内存数据类型分别执行不同的gc操作。
GC发生的时候,所有的线程都是会被暂停的。执行GC所占用的时间和它发生在哪一个Generation也有关系,Young Generation中的每次GC操作时间是最短的,Old Generation其次,Permanent Generation最长。
GC时会导致线程暂停,导致卡顿,Google在新版本的Android中优化了这个问题, 在ART中对GC过程做了优化揭秘 ART 细节 —— Garbage collection[5],据说内存分配的效率提高了10倍,GC的效率提高了2-3倍(可见原来效率有多低),不过主要还是优化中断和阻塞的时间,频繁的GC还是会导致卡顿。
上面就是Android系统内存分配和回收相关知识,回过头来看,现在各种手机厂商鼓吹人工智能手机,号称18个月不卡顿,越用越快,其实很大一部分Android系统的内存优化有关,无非就是利用一些比较成熟的基于统计,机器学习的算法定时清理数据,清理内存,甚至提前加载数据到内存。
二 ,Android常见内存问题和对应检测,解决方式
1. 内存泄露
不止Android程序员,内存泄露应该是大部分程序员都遇到过的问题,可以说大部分的内存问题都是内存泄露导致的,这里简单罗列下:
单例(主要原因还是因为一般情况下单例都是全局的,有时候会引用一些实际生命周期比较短的变量,导致其无法释放)静态变量(同样也是因为生命周期比较长)Handler内存泄露[7]匿名内部类(匿名内部类会引用外部类,导致无法释放,比如各种回调)资源使用完未关闭(BraodcastReceiver,ContentObserver,File,Cursor,Stream,Bitmap)对Android内存泄露业界已经有很多优秀的组件其中LeakCanary最为知名(Square出品,Square可谓Android开源界中的业界良心,开源的项目包括okhttp, retrofit,otto, picasso, Android开发大神Jake Wharton就在Square),其原理是监控每个activity,在activity ondestory后,在后台线程检测引用,然后过一段时间进行gc,gc后如果引用还在,那么dump出内存堆栈,并解析进行可视化显示。使用LeakCanary可以快速地检测出Android中的内存泄露。
没有更多推荐了,
加入CSDN,享受更精准的内容推荐,与500万程序员共同成长!}

我要回帖

更多关于 java 内存回收机制 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信