概述

从操作系统的角度来说,内存就是一块数据存储区域,属于可被操作系统调度的资源。现代多任务(进程)的操作系统中,内存管理尤为重要,操作系统需要为每一个进程合理的分配内存资源,所以可以从两方面来理解操作系统的内存管理机制。

第一:分配机制。为每一个进程分配一个合理的内存大小,保证每一个进程能够正常的运行,不至于内存不够使用或者每个进程占用太多的内存。

第二:回收机制。在系统内存不足打的时候,需要有一个合理的回收再分配的机制,以保证新的进程可以正常运行。回收的时候就要杀死那些正在占有内存的进程,操作系统需要提供一个合理的杀死这些进程的机制,以保证更少的副作用。

而作为一个多进程的操作系统,Android系统对内存的管理,也是有一套自己的方法的。跟PC不一样的是,Android作为一个移动操作系统,一般情况下,内存资源会比PC更少,所以就需要更加谨慎的管理内存。

Android中的内存管理机制

分配机制

Android为每个进程分配内存的时候,采用了弹性的分配方式,也就是刚开始并不会一下分配很多内存给每个进程,而是给每一个进程分配一个“够用”的量。这个量是根据每一个设备实际的物理内存大小来决定的。随着应用的运行,可能会发现当前的内存可能不够使用了,这时候Android又会为每个进程分配一些额外的内存大小。但是这些额外的大小并不是随意的,也是有限度的,系统不可能为每一个App分配无限大小的内除。

Android系统的宗旨是最大限度的让更多的进程存活在内存中,因为这样的话,下一次用户再启动应用,不需要重新创建进程,只需要恢复已有的进程就可以了,减少了应用的启动时间,提高了用户体验。

回收机制

Android对内存的使用方式是“尽最大限度的使用”,这一点继承了Linux的优点。Android会在内存中保存尽可能多的数据,即使有些进程不再使用了,但是它的数据还被存储在内存中,所以Android现在不推荐显式的“退出”应用。因为这样,当用户下次再启动应用的时候,只需要恢复当前进程就可以了,不需要重新创建进程,这样就可以减少应用的启动时间。只有当Android系统发现内存不够使用,需要回收内存的时候,Android系统就会需要杀死其他进程,来回收足够的内存。但是Android也不是随便杀死一个进程,比如说一个正在与用户交互的进程,这种后果是可怕的。所以Android会有限清理那些已经不再使用的进程,以保证最小的副作用。

Android杀死进程有两个参考条件:

进程优先级:

Android为每一个进程分配了优先级的概念,优先级越低的进程,被杀死的概率就更大。Android中总共有5个进程优先级。具体含义这里不再给出。

前台进程:正常不会被杀死

可见进程:正常不会被杀死

服务进程:正常不会被杀死

后台进程:存放于一个LRU缓存列表中,先杀死处于列表尾部的进程

空进程:正常情况下,为了平衡系统整体性能,Android不保存这些进程

回收收益:

当Android系统开始杀死LRU缓存中的进程时,系统会判断每个进程杀死后带来的回收收益。因为Android总是倾向于杀死一个能回收更多内存的进程,从而可以杀死更少的进程,来获取更多的内存。杀死的进程越少,对用户体验的影响就越小。

官方推荐的App内存使用方式是什么样的?

1、当Service完成任务后,尽量停止它。

因为有Service组件的进程,优先级最低也是服务进程,这会影响到系统的内存回收。IntentService可以很好地完成这个任务。

2、在UI不可见的时候,释放掉一些只有UI使用的资源。

系统会根据onTrimMemory()回调方法的TRIM_MEMORY_UI_HIDDEN等级的事件,来通知App UI已经隐藏了。

3、在系统内存紧张的时候,尽可能多的释放掉一些非重要资源。

系统会根据onTrimMemory()回调方法来通知内存紧张的状态,App应该根据不同的内存紧张等级,来合理的释放资源,以保证系统能够回收更多内存。当系统回收到足够多的内存时,就不用杀死进程了。

4、检查自己最大可用的内存大小。

这对一些缓存框架很有用,因为正常情况下,缓存框架的缓存池大小应当指定为最大内存的百分比,这样才能更好地适配更多的设备。通过getMemoryClass()和getLargeMemoryClass()来获取可用内存大小的信息。

5、避免滥用Bitmap导致的内存浪费。

根据当前设备的分辨率来压缩Bitmap是一个不错的选择,在使用完Bitmap后,记得要使用recycle()来释放掉Bitmap。使用软引用或者弱引用来引用一个Bitmap,使用LRU缓存来对Bitmap进行缓存。

6、使用针对内存优化过的数据容器。

针对移动设备内存有限的问题,Android提供了一套针对内存优化过的数据容器,来替代JDK原生提供的数据容器。但是缺点就是,时间复杂度被提高了。比如SparseArray、SparseBooleanArray、LongSparseArray、

7、意识到内存的过度消耗。

Enum类型占用的内存是常量的两倍多,所以避免使用enum,直接使用常量。

每一个Java的类(包括匿名内部类)都需要500Byte的代码。

每一个类的实例都有12-16 Byte的额外内存消耗。

注意类似于HashMap这种,内部还需要生成Class的数据容器,这会消耗更多内存。

8、抽象代码也会带来更多的内存消耗。

如果你的“抽象”设计实际上并没有带来多大好处,那么就不要使用它。

9、使用nano protobufs 来序列化数据。

Google设计的一个语言和平台中立打的序列化协议,比XML更快、更小、更简单。

10、避免使用依赖注入的框架。

依赖注入的框架需要开启额外的服务,来扫描App中代码的Annotation,所以需要额外的系统资源。

11、使用ZIP对齐的APK。

对APK做Zip对齐,会压缩其内部的资源,运行时会占用更少的内存。

12、使用多进程。

一个符合Android内存管理机制的App应该是什么样的?

一个遵循Android内存管理机制的App。应该具有如下几个特点:

1、更少的占用内存。

2、在合适的时候,合理的释放系统资源。

3、在系统内存紧张的情况下,能释放掉大部分不重要的资源,来为Android系统提供可用的内存。

4、能够很合理的在特殊生命周期中,保存或者还原重要数据,以至于系统能够正确的重新恢复该应用。

App为什么要符合该内存管理机制?这样做有什么好处?

一个遵循Android的内存管理机制的App,在Android系统中,就是一个好的公民,那么系统自然是倾向于保护这些良民,而去杀死那些素质不高的人。所以符合Android内存管理机制,对Android系统和App来说,是一个双赢的过程。如果每一个App都遵循这种规则,那么Android系统就会更加流畅,也会带来更好的体验,而App可以更长时间的驻留于内存中。

在这种管理方式下,如何编写符合Android内存管理机制的App?

主要是参考官方推荐的内存使用方式,来设计和编写App。

避免创建不必要的对象。

在合适的生命周期中,合理的管理资源。

在系统内存不足时,主动释放更多的资源。

编写Android应用时,如何更少的使用内存资源?

避免创建不需要的对象。

比如使用StringBuffer来代替很多个String相加的操作。

使用原始类型来代替包装类型,int比Integer占用更少的资源。

两个并行的属性数组,优于一个包含这两个属性的对象的数组。这个在设计数据容器的时候会有意义,比如类A有两个属性A(int, String),使用 int[] 和 String[] 优于 A[]。

使用常量代替enum。

少用包装类,能够使用原始类型的,就使用原始类型。

App如果真的需要很多内存怎么办?

多进程

把消耗内存过大的模块,或者需要长期在后台运行的模块,移入到单独的进程中运行。Android会为每一个进程单独分配内存,所以理论上App就可以使用到更多的内存。但是多进程是一把双刃剑,错误的使用,会带来其他很多的问题,这里不再详细谈这个话题。

申请大内存

在<application>标签中,把largeHeap设置为true,Android系统会为该应用额外分配内存。但是不要滥用这个方法。如果一个App真的需要大内存,比如需要打开很多大图片的应用,可以使用这种方式。千万不要因为OOM而使用这种方法,这个时候更应该去检查App的代码是否不合理。

开发人员应该注意的App内存管理方式?

内存溢出

内存溢出,就是OOM,也就是内存不够用了。有一个典型的例子就是加载了很多没有经过压缩的Bitmap到内存中,这些Bitmap很大,但是又真的在被使用,必须要在内存中,所以这个时候内存就不够用了。这个时候,App再申请更多内存的时候就不行了,系统会抛出OOM。

解决这种问题:1、减少每个对象占用的内存,比如压缩图片。2、申请大内存。

内存泄露

内存泄露,就是Memory Leak,也就是本来该被GC回收后还给系统的内存,并没有被GC。多数是因为不合理的对象引用,当一个对象不再使用的时候,由于代码问题,没有正确的释放引用,就导致了内存泄露。

解决这种问题:1、通过各种内存分析工具,比如MAT,分析运行时的内存映像文件,找出造成内存泄露的代码,然后修改掉。2、适当的使用WeakReference。

Android中的内存管理机制以及正确的使用方式的更多相关文章

  1. cocos2dx中的内存管理机制及引用计数

    1.内存管理的两大策略: 谁申请,谁释放原则(类似于,谁污染了内存,最后由谁来清理内存)--------->适用于过程性函数 引用计数原则(创建时,引用数为1,每引用一次,计数加1,调用结束时, ...

  2. 【转】Android中的内存管理--不错不错,避免使用枚举类型

    原文网址:http://android-performance.com/android/2014/02/17/android-manage-memory.html 本文内容翻译自:http://dev ...

  3. Python中的内存管理机制

    Python是如何进行内存管理的 python引用了一个内存池(memory pool)机制,即pymalloc机制,用于管理对小块内存的申请和释放 1.介绍 python和其他高级语言一样,会进行自 ...

  4. 正确认识Android的内存管理机制,合理关闭进程 (一)

    随着大家收货后会有很多乐粉晒内存,为啦方便大家,在网上搜集了一些相关Andriod管理的相关机制合理管理内存,整理下发个贴. 首先要知道Android系统是基于Linux 2.6内核开发的开源操作系统 ...

  5. IOS中内存管理机制浅解

    我们知道在程序运行过程中要创建大量的对象,和其他高级语言类似,在ObjC中对象时存储在堆中的,系统并不会自动释放堆中的内存(注意基本类型是 由系统自己管理的,放在栈上).如果一个对象创建并使用后没有得 ...

  6. 2、COCOS2D-X内存管理机制

    在C++中.动态内存分配是一把双刃剑,一方面,直接訪问内存地址提高了应用程序的性能,与使用内存的灵活性.还有一方面.因为程序没有正确地分配与释放造成的比如野指针,反复释放,内存泄漏等问题又严重影响着应 ...

  7. C++中的内存管理

    在C++中也是少不了对内存的管理,在C++中只要有new的地方,在写代码的时候都要想着delete. new分配的时堆内存,在函数结束的时候不会自动释放,如果不delete我分配的堆内存,则会造成内存 ...

  8. Cocos2d-x开发中C++内存管理

    由于开始并没有介绍C++语言,C++的内存管理当然也没进行任何的说明,为了掌握Cocos2d-x中的内存管理机制,是有必要先了解一些C++内存管理的知识.C++内存管理非常复杂,如果完全地系统地介绍可 ...

  9. Spark Tungsten in-heap / off-heap 内存管理机制--待整理

    一:Tungsten中到底什么是Page? 1. 在Spark其实不存在Page这个类的.Page是一种数据结构(类似于Stack,List等),从OS层面上讲,Page代表了一个内存块,在Page里 ...

随机推荐

  1. docker device or resource busy

    docker-compose -f docker-compose.yml up -d  时候报错 device or resource busy 使用 docker-compose down 会导致一 ...

  2. Kvm 简介 安装 使用 桥接网络

    KVM 全称是 基于内核的虚拟机(Kernel-based Virtual Machine),它是一个 Linux 的一个内核模块,该内核模块使得 Linux 变成了一个 Hypervisor: 它由 ...

  3. ORB-SLAM2 论文&代码学习 —— LoopClosing 线程

    转载请注明出处,谢谢 原创作者:Mingrui 原创链接:https://www.cnblogs.com/MingruiYu/p/12369339.html 本文要点: ORB-SLAM2 LoopC ...

  4. 进阶之路 | 奇妙的Window之旅

    前言 本文已经收录到我的Github个人博客,欢迎大佬们光临寒舍: 我的GIthub博客 学习清单: Window&WindowManagerService Window&Window ...

  5. 01、Git安装教程(windows)

    首先如下图:(点击next) 第二步:文件位置存储,可根据自己盘的情况安装 第三步:安装配置文件,自己需要的都选上,下一步 第四步:不创建启动文件夹,下一步: 第五步:选择默认的编辑器,我们直接用推荐 ...

  6. jQuery on 绑定的事件 执行两次

    $(".class1").on("click",".class2",function(){ alert('提示'); }); 上面代码,怎么 ...

  7. OpenLayers要素拖拽

    //拖拽要素 function dragFeature (_map,_dragEndCallback) { let selFeature = null; _map.on("pointerdr ...

  8. TortoiseGit 绑定 GitHub/Gitee ssh秘钥

    小乌龟生成私钥和公钥 打开PuTTYgen 生成公钥/私钥文件 打开Pageant添加私钥.ppk文件 打开公钥文件获取key 打开GitHub/Gitee添加公钥 Gitee GitHub

  9. MySql学习-1.MySql的安装:

    1.安装包的下载(mysql-v5.7.25 )(NavicatforMySQL_11.2.15): 链接:https://pan.baidu.com/s/166hyyYd3DMjYhMwdW805F ...

  10. Centos 7.5 搭建FTP配置虚拟用户

    Centos 7.5 搭建FTP配置虚拟用户 1.安装vsftpd #vsftpd下载地址 http://mirror.centos.org/centos/7/os/x86_64/Packages/v ...