精华内容
下载资源
问答
  • 低端android手机内存优化

    千次阅读 2012-11-05 09:57:32
    大家好,今天我主要来和大家交流下低端android手机内存优化的问题。 一、问题的引出 前天,我在论坛发了一个帖子,想请教大家关于联想A68e内存优化的问题,但是回复者寥寥无几,课件也很少有机油对这方面有较深入...

    大家好,今天我主要来和大家交流下低端android手机内存优化的问题。

    一、问题的引出

    前天,我在论坛发了一个帖子,想请教大家关于联想A68e内存优化的问题,但是回复者寥寥无几,课件也很少有机油对这方面有较深入的学习了解。我今天中午,查了有关资料,也用了自己的手机进行了测试,觉得可能对A68e(其实是广大低端Android手机)用户有点帮助,所以特地来分享以下。(说明:本人用的是SumsumgI9103,我媳妇用的是电信套餐的联想A68e,这几天我没拿到她的手机来测试,所以只能自己的手机进行测试,但是我觉得还是具有一定的参考价值的)。

    二、ROMRAM

    首先,我先解释一下ROMRAM的区别。

    ROMReadOnly Memory,即只读存储器;RAMAccessRandom Memory,即随即读写存储器。

    ROM是存储程序和数据的,类别电脑的硬盘,可以存放安装的程序、文件、数据等。而论坛中有开AppEXT2的方法(我没试过),那个只是节省出更多的ROM空间,这样可以使程序运行得更为流畅,但是不能解决“同时运行程序的数量最大值太小”的问题。以A68e为例,如果开一个QQ、音乐播放器,再开个UC浏览器估计机子就崩溃而导致程序退出。

    RAM才是程序运行时所占用的物理空间。RAM的价格比ROM贵很多,所以RAM的大小一半程度上决定了机子的价位(另一半就是CPU)。我的Sumsung1GRAM,同时开QQQQ游戏、QQ音乐、浏览器、微信等七、八个程序也毫无压力。所以RAM太小是影响A68e(包括很多Android手机)用户体验的原因。如果你是个游戏玩家、手机达人,那么这类机子一定不适合你。如果你希望能像有高端机那样的用户体验,我建议你还是多话点银子购买配置高的Android手机。

    关于官方宣传256RAM实际上只有170,那是有一部分被Android系统占用。我的手机1GRAM,实际上也只有724M

    三、Android系统内存管理机制及进程调度机制

    下面开始具体的分析了。

    首先Android系统是基于Linux 内核开发的开源操作系统,linux系统的内存管理有其独特的动态存储管理机制。Android采取了一种有别于Linux的进程管理策略,Linux系统在进程活动停止后就结束该进程,而Android把这些进程都保留在内存中,直到系统需要更多内存为止。这些保留在内存中的进程通常情况下不会影响整体系统的运行速度,并且当用户再次激活这些进程时,提升了进程的启动速度。

    Android系统这样的设计不仅非常适合移动终端(手机、平板)的需要,而且减少了系统崩溃的可能,确保了系统的稳定性。老想着清理内存的同学完全是因为被塞班或者Windows毒害太深,事实上,经常用Taskiller之类的软件关闭后台所有进程,很容易造成系统的不稳定。很多时候出现问题了,只要重启就能解决的原因也在于此。

    广大机油一定会发现,关闭了QQ、微信等程序后,其实并没有完全关闭这些程序。这些程序在后台占用了一定的内存,但是并没有运行时(或者说运行在前台时)占用得多。而且这些程序总会自己启动,清理过内存后又会再次出现。

    Android系统有六类进程:前台进程、后台服务、可见进程、隐藏程序、空进程、内容提供者。那Android系统什么时候结束进程?结束哪个进程呢?如果你懂java,就会更容易理解Android系统的内存管理机制。与java的垃圾回收机制类似,系统有一个规则来回收内存。内存调度依据各类进程的“阈值”设置,只有低于这个值,系统才会按一个列表来关闭用户不需要的东西。而这个列表中进程排列的先后,是依据LRULeastRecentlyUsed 最近最少使用)进程调度方法。即当为各类进程分配的RAM小于各类进程设置的阈值时,最近最少使用的进程,将会最先被关闭。

    四、Android系统的六大类进程

    (这里我是参考了其他的地方资料,根据自己的理解改写,可能有误,请指正。)

    1.前台进程:目前正在屏幕上显示的应用进程和一些系统进程。举例来说,当你运行一个程序,如浏览器,当浏览器界面在屏幕显示时,浏览器属于前台进程,但一旦你按home键回到主界面,浏览器在后台运行,并且依然可见。我们最不希望终止的进程就是前台进程。

    2.后台服务:目前正在运行的一些服务(绝大部分的系统进程)。举例来说:拨号、联系人等,这部分服务虽然属于后台服务,但很一些系统功能依然息息相关,我们时常需要用到它们,是不可能被进程管理终止的。(后台服务不属于后台程序)。

    3可见进程(后台进程):后台进程是一些不再前台,但用户依然需要用到的进程,举例来说,后台运行的QQ(需要随时接受消息)、音乐播放器等。我个人认为可见进程就是后台进程!这部分进程虽然不在前台,但与我们的使用也密切相关,我们也不希望它们被终止。(塞班智能机,长按功能键就能显示目前正在运行的应用程序,大家都记得吧?)

    4.隐藏进程:个人认为是一些应用程序提供的服务!比如输入法、QQ、飞信、QQ音乐即使退出后,亦可以发现他们仍在运行,只是占用的内存没有运行时那么多。如果此时QQ上有人找你的话,你是无法接受到的。但是这些服务在运行具体干什么,我也不太清楚,可能可以加快启动速度,而我认为也可能跟推送消息有关吧(使用飞信的同学一定明白我说的这句话!)。对于RAM太小的我们,这些进程是我们希望被终止的。而对于RAM足够的大机子,没有必要关闭。

    5.空进程:没有任何东西在内运行的进程,依然会在进程中驻留一个空进程,这个进程里没有任何数据在运行,作用往往是提高该程序下次的启动速度或者记录程序的一些历史信息。这部分进程无疑是应该最先终止的。

    6.内容提供者:没有程序实体,只是为别的程序提供内容来使用的,比如日历等。(虽然日历是系统程序,但是可以终结)。在终止进程时,这类程序应该有较高的优先权。

    关于可见程序和隐藏程序我想举个例子。

    当我运行QQ时(不论后台、前台),用系统的任务管理器是可以查看到QQ正在运行。当我退出后,任务管理器显示没有任务,但是用第三方的进程管理器查看,还是可以发现QQ进程,该进程应该是隐藏进程,可能还包含空进程。细心的话,大家都是可以发现的。

    五、设置阈值,优化内存。

    下面用两种情景来说明:

    情景一:我希望可以边听音乐边聊QQ,还希望在QQ没说话的时候能用浏览器看新闻。音乐播放程序始终在后台运行,偶尔需要调出来换歌。而QQ和浏览器可能需要频繁切换。我们希望这三个程序都不会因为RAM不够而退出,对吧?

    情景二:我想玩一个稍大的游戏,这时候你可能会把内存清理一下(清理隐藏程序)。但是你希望有更方便的方法让这些自动关闭,是吧?(QQ、飞信、音乐所提供的服务,还有一些不常用的程序提供的服务,比如钛备份、ES文件浏览器等等,甚至是从系统程序中释放一些内存比如日历、权限管理等,虽然这些单个占内存不多,但是对我们RAM小的人弥足珍贵)。

    那什么时候完全关闭该程序呢?怎样让它按照我们的意愿自动关闭我们想关闭的程序,而不是一遍一遍的全杀任务呢?这取决于该程序的类型(就是上面讲的那几个类型)。系统会给每个类型的程序一个内存阈值(阀门),也就是说当运行内存低于某个值时,系统会自动按照打开的先后顺序来关闭该类型的程序。例如,当运存小于24MB时,系统才会自动关闭空进程这一类型的程序。我们可以提高这一阈值,比如100M,那么RAM小于100时,系统就会关闭这些空进程。

      前台程序、后台服务、可见程序是与用户体验息息相关的内容,这部分的进程管理策略要相对保守,要给这些进程留下足够的运行空间,而压榨无用进程,腾出内存空间给主要程序。使用下面就来介绍设置阈值的方法。我们这里不介绍配置系统文件的方法,而使用软件的方法更为简便。我介绍的使用的是安卓优化大师里的内存管理插件(无需下载,进入界面后向左滑动屏幕,在工具栏里)。

    系统默认

    前台程序8MB 后台服务16MB

    可见程序 12MB 隐藏程序24MB

    空程序 32MB 内容提供者28MB

    可以看到,系统默认设置中各类进程的内存阀值相当接近。在实际程序运行中,很容易导致多种类型的进程同时被关闭。

    游戏玩家/重度浏览器使用者配置

    前台程序6MB 后台服务16MB

    可见程序 8MB 隐藏程序80MB

    空程序 100MB 内容提供者90MB

    用户特点:长时间专注于某一特定的,高内存需求的程序,对多任务的需求不高

    配置理念:压榨隐藏进程(不是后台服务)、内容提供者和空进程,将内存尽可能多得留给前台进程(大型游戏、浏览器)和系统提升游戏速度和浏览器体验

    优点:程序启动和运行的速度最快

    缺点:多任务处理不理想,开启较多程序时,后台进程(可见进程)会被终止。

    个人分析:此配置适用于情景二。游戏是我们的前台进程,当我们玩游戏时,一般不会还开着多个后台进程(QQ、音乐等),所以可见程序(后台程序)的阈值没有必要改变。如果你要硬开着,那么游戏肯定是无法启动的!注意,并不能说此配置不适用于情景一!因为三个程序并不是很多!

    多任务配置

    前台程序6MB 后台服务16MB

    可见程序 8MB 隐藏程序20MB

    空程序 100MB 内容提供者60MB

    用户特点:同时运行多个应用程序,需要经常在多个程序间切换

    配置理念:压榨空进程,给内容供应节点留有一定空间,最大限度提升后台程序的使用空间,提升多任务的处理能力

    优点:运行多个程序时,由于可支配内存较多,后台程序不容易被终止

    缺点:程序启动的速度和整体系统的运行速度可能会比游戏玩家配置略慢一些,由于经常运行多任务,平时系统的响应速度会受到一定影响

    个人分析:多任务配置和上一种配置主要差别在于隐藏程序的阈值设置。隐藏程序的存在不仅可以提供服务,并且能加快首次启动程序的速度。我建议如果只是常用34种软件,那么就使用游戏玩家/重度浏览器使用者配置。当经常地开启较多任务时,才开启多任务配置。所以我认为对于低端android机,游戏玩家/重度浏览器使用者配置是最佳的选择!!!!

    轻度用户/女生专用配置

    前台程序6MB 后台服务16MB

    可见程序 8MB 隐藏程序24MB

    空程序 48MB 内容提供者32MB

    用户特点:手机的主要功能是短信和电话,偶尔用用相机自拍

    配置理念:压榨空进程,给内容供应节点留有一定空间,最大限度提升后台程序的使用空间,提升多任务的处理能力

    优点:比较均衡的配置,提升了系统的可用内存,使得系统的整体速度得到了提高,拉开了各级进程的管理策略层次,使得管理机制更有效率

    缺点:比较均衡的配置,无明显缺点

    个人分析:此配置对低端Android机没有明显作用!

    六、总结

    对于A68e用户(或者说低端Android机),我建议使用游戏玩家/重度浏览器使用者配置。为什么这么说。因为此配置对隐藏程序的阈值较高。像钛备份、ES文件浏览器等软件我们只是偶尔使用,但是当我们完全退出后,他们仍然存在与内存中。这些是我们需要系统来自动关闭的。而对于QQ、音乐等隐藏程序,这些是我们经常使用的,即使他们存在于内存中,也没有太大关系(除非是玩大型游戏)。而且同时开34个进程,不算太多,游戏玩家/重度浏览器使用者配置足够用了!

    当然,阈值除了使用这些标准配置外,还能自己调节,希望广大机油一起来测试,为我们的A68e跳出最佳的配置,获得最好的用户体验。

    因为我还没有拿到媳妇的机子,我自己测试我的I9103,游戏玩家/重度浏览器使用者配置的确可以释放出更多的内存空间。我通过比较默认配置和游戏玩家/重度浏览器使用者配置下刚开机时的内存占用量,后者要比前者少将近40MB。对于A68e,也有机油说过,原本只剩20MB,更改配置后多出了50MB的内存空间!

    七、题外话

             开启appext2只是腾出了更多的ROM空间,这样是可以安装较多的软件的。并不能解决RAM太小而造成的问题。有人说开启appext2,玩极品飞车流畅到爆,我严重怀疑其真实性。流畅跟ROM关系不大,而是跟RAMCPU、显卡有密切关系。那Iphone3GSIphone4比较,同样的RAM大小,但是其性能无法相比的,关键就是CPU的速度差异。

             关于SWAP

             SWAP是一种基于内存分页的交换分区,类似虚拟内存技术,作为物理内存不足时的扩展补充,但并不等于是物理内存的直接增量。这才是解决RAM小的唯一办法,这可惜A68e的内核不支持SWAP


    展开全文
  • 因此需要调查相机场景下的内存优化方法。 memcg调研 memcg的好处: 1 监控进程使用内存 站在一个普通Linux开发者的角度,如果能控制一个或者一组进程所能使用的内存数,那么就算代码有bug,内存泄漏也不会对系统...

    一 背景

        现在安卓手机在相机操作下,不可避免要大量消耗内存资源,同时当今手机如同电脑一样,前台相机,后台各种app进程,都会消耗内存。因而会出现相机场景下,系统可用内存不足,内存压力增加不少,对相机性能会带来严重的影响。因此需要调查相机场景下的内存优化方法。

    二 memcg调研

    memcg的好处:

    1 监控进程使用内存

    站在一个普通Linux开发者的角度,如果能控制一个或者一组进程所能使用的内存数,那么就算代码有bug,内存泄漏也不会对系统造成影响,因为可以设置内存使用量的上限,当到达这个值之后可以将进程重启。

    我们可以通过cgroup的事件通知机制来实现对内存的监控,当内存使用量穿过(变得高于或者低于)我们设置的值时,我们的程序就会收到通知。
    ps: 应该是用Memory thresholds接口来进行监控的。

    有个实际应用场景就是:比如可以我们的camera程序代码中设置监听接口,如果camera相关进程耗内存大于设置的阀值时,camera程序会及时收到相应的事件通知。

    2 抑制进程使用内存

    当某个mem cgroup里面物理内存达到上限(memory.limit_in_bytes)后,系统的默认行为是kill掉该cgroup中继续申请内存的进程,那么怎么控制这样的行为呢?答案是配置memory.oom_control。
    这个文件里面包含了一个控制是否为当前cgroup启动OOM-killer的标识。如果写0到这个文件,将启动OOM-killer,当内核无法给进程分配足够的内存时,将会直接kill掉该进程;如果写1到这个文件,表示不启动OOM-killer,当内核无法给进程分配足够的内存时,将会暂停该进程直到有空余的内存之后再继续运行。
    ps: memcg里面可以有选择的开启oom-killer,当不启动oom-killer时,如上面说的,就可以达到抑制进程内存使用效果吧。

    3 体验系统对整机内存的优化思路

    从kernel memcg的设计思路,结合手机Android场景,可以看到整机内存分3大块:

    1) 用户态进程管理的内存

    典型的特点是该内存可以在系统有内存压力时,被系统随时回收掉的。具体思路就是该内存必须挂到内核的Lru链表上,然后文件页或者被回写,或者被抛弃掉,匿名页被压缩写到zram中。

    另外该内存可以被oom时通过杀进程方式直接释放掉的。

    2) 内核管理的内存

    比如内核态文件系统做io操作自身申请的内存,还有进程fork时需要内核申请的内存等这些都是属于内核管理的内存。

    这部分内存直接特点就是:cached部分可以在有系统内存压力时,被回收掉(通过那个shrink_slab函数来做),非cached部分则无论怎么有系统压力,也不能被回收掉了。

    所以内核态不要出现内存泄露问题,否则系统很难纠正。用户态出现内存泄露了,系统可以纠正错误的,直接oom或者通过Lru链表被匿名页回收了。

    这部分内存管理,memcg是通过专门的memcg.kmem节点进行管理的,有区别于1)中的内存memcg管理。

    3) 一些ion或者gpu驱动自身申请的内存

    前两部分,内核态的内存管理都能cover到,memcg也能负责管理到。但是第3部分内存,memcg不能管理到,内核态内存管理仅仅在有内存压力时,会释放掉其中的cached内存。

    并且这部分代码多位于driver/stanging目录下面,不是内核主线代码,所以内核内存管理方面对这部分也关注的不多,会有些bug问题的。

    memcg缺陷:

    1 通用的缺陷

    当一个进程从一个cgroup移动到另一个cgroup时,默认情况下,该进程已经占用的内存还是统计在原来的cgroup里面,不会占用新cgroup的配额,但新分配的内存会统计到新的cgroup中(包括swap out到交换空间后再swap in到物理内存中的部分)。
    我们可以通过设置memory.move_charge_at_immigrate让进程所占用的内存随着进程的迁移一起迁移到新的cgroup中。
    注意:迁移内存占用数据是比较耗时的操作。还有可能会失败的。

    ps: 系统缺省情况下,没有设置这个memory.move_charge_at_immigrate。因为如上面说的,频繁更换mem cgroup的话,会导致内核态工作异常耗时。

    进一步确认:

    xref: /Android-r/kernel/msm-4.19/Documentation/cgroup-v1/memory.txt文档里面核对过,上面的说法是正确的
    (Note: If we cannot find enough space for the task in the destination cgroup, we try to make space by reclaiming memory. Task migration may fail if we cannot make enough space.
    Note: It can take several seconds if you move charges much.)

    2 安卓场景下的特有缺陷

    安卓里面有很多的多进程间的共享页,比如app创建后要和zygote进程共享很多内存页。这个memcg并没有提供对共享页的特殊操作接口,只是规定:由多个 memcg 共享的一页内存被交换出去。当该页被换回时,它会被计数到起初分配它的 memcg,即使这个 memcg 已经很久不用该页。

    这样的话,如果某进程要动态迁移到其他新group时,如果不设置move_charge_at_immigrate的话,该进程原来分配的内存页就不能有效继续监控或者限制了。

    设置这个move_charge_at_immigrate的话,那么跟原group内其他重要进程,比如zygote共享的内存页也会随之迁移到新group内,虽然zygote进程并没有迁移到新group内。

    优化方法1:前后台分组调研

    最理想的优化方法是对手机实现前后台分组,相机前台操作时,能够动态回收和抑制住后台app的内存使用,而不是一味的杀掉后台所有app。

    其实结合上面提出的memcg缺陷,可以推测出:

    为啥谷歌和高通在手机Android系统上不推崇设置memcg前后台分组。即使io,cpu现在最新的安卓r版本都已经做到了前后台分组了。估计是因为Android上面前后台进程不像互联网公司那种docker环境一样,对进程进行分组后,该进程估计会比较稳定地呆在这个组里面的。
    Android里面前后台里面进程在时刻的迁移变化,一旦某进程变化了自己的归属mem cgroup,不做内存迁移的话,会有下面提到的问题。做内存迁移的话,就会比较耗时了。

    进一步结合下面搜到的社区邮件讨论,可以再详细地发掘到高通不做memcg前后台分组的原因。

    [PATCH] memcg: Provide knob for force OOM into the memcg

    https://lkml.org/lkml/2014/12/16/190

    该邮件讨论的是高通开发提的memcg patch内容,高通意识到在新进程要加入到新mem cgroup里面的话,如果该group已经达到limit的话,新进程要加入到该group就困难了。因此高通提了这个patch,想在进程加入group之前,先杀掉该group里面的几个进程,给新加入者腾点内存空间。

    下面摘取了跟后来高通为啥不搞memcg前后台分组原因相关的重要讨论:

    1) Johannes Weiner‘s reply:

    Rather than scanning thousands or millions of page table entries to relocate a task and its private memory to another configuration domain, wouldn't it be easier to just keep the task in a dedicated cgroup and reconfigure that instead?
    ps: 迁移task到新group时,需要移走task原来的内存页,这个比较耗时啊。

    2) 高通开发回复:
    Your suggestion is good. But in specific cases, we may have no choice but to migrate.

    Take a case of an Android system where a process/app will never gets killed until there is really no scope of holding it any longer in RAM.
    So, when that process was running as a foreground process, it has to belong to a group which has no memory limit and cannot be killed. Now, when the same process goes into background and sits idle, it can be
    compressed and cached into some space in RAM. These cached processes are ever growing list and can be capped with some limit. Naturally, these processes belongs to different category and hence different cgroup which just controls such cached processes.
    ps: 说的是高通意识到需要对手机上的进程进行内存前后台分组,前台mem cgroup里面没有mem limit,后台group里面需要设置mem limit,以抑制后台进程使用内存。但是这种前后台分组肯定需要做task migrate.

    3) Johannes Weiner‘s reply:

    Likewise, if you move the task but fail to migrate some pages, do you leave them behind in the foreground group where they are exempt from reclaim?
    ps: 这个又说了task migrate的弊端。说的可能就是为啥高通后来不想进行内存前后台分组的原因了。

    然后提出了per_app_memcg:
    Your configuration can be rephrased using this: by putting all apps into their own groups, and setting the lower boundary to infinity for the foreground apps and to zero for the background apps, the kernel will always reclaim and OOM kill the background apps first.

    重点总结

    ps: 整个邮件大致意思是ohannes Weiner‘s建议高通放弃做Android上的memcg前后台分组,告知了若干风险点后,又建议高通开发者做一个per_app_memcg的方案,这样可以成功利用memcg也可以达到抑制后台进程的内存使用。

    所以结合上面的一些资料查阅,可以看到在手机做memcg前后台分组是比较麻烦,有风险的。

    优化方法2:per_app_memcg

    这个特性我看了,大致思路是从app进程一创建时就进行memcg分组,而且是每个app进程一个分组,系统有多少个app,就有多少个分组。刚创建时就分组,这样可以避免上面提的进程迁移group的缺陷问题。

    这样分组后的好处:

    系统可以根据每个app的adj值,评估该app的重要性,从而对每个app都可以设置不同的该group使用内存最大上限。一旦超过该上限后,系统就会迫使该app自身做内存回收工作,或者超过hard limit时,还可以借助memcg抑制住该进程的内存使用。

    这样在相机场景下,一旦抑制住手机后台进程的内存使用,对于相机自身性能提升,还有减少相机后台杀进程方面都有大的帮助。

    不好的地方:

    分的组太多,造成内核层每个group的lru链表都比较短,下面会提的由此造成的负作用。

    Android r版本上实现per_app_memcg的思路:

    xref: /android-r/system/core/libprocessgroup/processgroup.cpp
    里面createProcessGroup函数里面引入了UsePerAppMemcg,并且可以创建per app级别的uid_pid_path,还做了 setProcessGroupSoftLimit函数。

    ps: libprocessgroup库里面实现了创建per_app_memcg的接口函数。

    /android-r/frameworks/base/core/jni/com_android_internal_os_Zygote.cpp#1703里面:
    大概是每进程创建成功时,会call createProcessGroup(uid, getpid())去创建对应的per_app_memcg。

    lmkd里面会设置完每app group的soft limit,最终在内核发挥作用是靠:mem_cgroup_soft_limit_reclaim这个函数,它在balance_pgdat函数(kswapd触发回收)和shrink_zones函数(直接内存回收)中均被调用到了,就是具体在调用shrink_node_memcg去回收系统全局的内存页之前,先回收下每app memcg中的超过soft limit的内存页。。

    ps: 那这么大致看来,内核文档里面说soft limit是仅仅在系统有内存压力时,才会做超过该limit的group里面的内存回收。上面的内存压力触发,就是指的kswapd回收和直接内存回收被触发了。

    per_app_memcg的缺陷

    Query on per app memory cgroup

    https://www.spinics.net/lists/linux-mm/msg121665.html

    这是高通望社区提的实现per_app_memcg功能的邮件讨论,里面提到应用per_app_memcg的弊端:

    Is there a way to mitigate this problem of small lru sizes, priority drop and kswapd cpu consumption.

    ps: 上面说的就是per_app_memcg应用的负作用。

    Soft limit setting can be tricky, but my advise is to set it based on how much you see a particular cgroup using when the system is under memory pressure.
    这个地方说了设置soft limit的弊端吧。

    总结

    可能是考虑到上面的负作用,所以最后仅仅在low ram device上部署了per_app_memcg功能

    三 app compact调研

    从上面看到,在Android平台上部署memcg分组有很多局限性,但是为了达到能够成功抑制后台进程使用内存,解放前台进程内存使用压力,高通不得不仅在low ram device上部署了memcg分组。

    但是非low ram device设备上,如何比如在相机场景下,做到抑制后台进程内存使用,解放相机进程使用内存压力呢?

    安卓r版本上,看到谷歌和高通的发力点:一个是基于psi的用户态lmkd杀进程,另外一个就是app compact。

    lmkd杀进程缺陷

    lmkd杀进程这个是把内核态的lowmem killer上移到用户态,通过psi提前感知到系统内存分配压力,及时杀进程,来释放内存压力。

    这样的思路虽然没问题,杀进程的确是最快地释放内存压力的方法。但是实际应用中,存在以下问题:

    1:后台app进程有各种保活黑科技,严重情况下,比如在双开场景中,曾出现过高adj值的双开进程频繁被杀后,再被重启。这样lmkd一直杀该进程,从而阻挡住了lmkd杀其他进程,这种场景中,杀进程策略等同于失效了。

    2:后台三方app有很多手段会突破系统封锁,到达adj visible和perce级别,这样persistent,visible和perce 3个级别的进程数量和耗内存量越来越多,同时lmkd不敢轻易杀这个级别的进程,以免被用户投诉,可感知重要app没了。

    所以光靠lmkd杀进程来达到优化内存,提升相机性能还不行。

    app compact

    app compact是基于以前内核里面就有的process reclaim接口,谷歌和高通对其做了优化,重点是fwk层对这个接口做了封装,可以在进程adj发生变化时,对低优先级进程进行内存回收,从而为高优先级进程提供更多的可用内存。

    使用app compact的好处

    1:属于通用的主动内存回收的好处,能够在内存降低低阀值前,对内存进行主动回收,可以使得lmkd杀进程的水位线提升(具体到lmkd代码是other_free进一步增多),这样使得系统杀进程阀值提高,减少杀后台高优先级进程的概率。

    2:可以在用户态配置策略,挑选低优先级进程进行回收, 避免对内存进行全局回收,从而影响到高优先级进程的内存使用。

    不足之处:

    1:进行匿名页回收时,会比较耗时点,和回收干净文件页相比。另外一旦做完回收后,如果进程又继续活跃使用内存,那么被回收的内存会被page refault,这样前面做的回收工作会部分失效。

    2:只是在进程级别,进程粒度上对内存进行回收的,对于系统中unused内存页无法及时free掉(比如已死亡进程,但是还残留在pagecache中的内存页)。

    3:对进程做app compact时,该进程会进入到D状态,被block住,而且回收时,和直接杀进程相比,是要多耗点cpu资源的。

    app compact的有效部署

    谷歌为了降低app compact耗时,以及减少对系统的负面影响,在fwk ams模块层做了不少工作。详见下面文件中MemCompactionHandler的具体实现:

    /android-r/frameworks/base/services/core/java/com/android/server/am/CachedAppOptimizer.java

    还有同目录的oomadjuster.java文件里面的筛选待回收的进程方面工作。

    同时内核层process reclaim接口里面也有待优化点,比如限制只有page_mapcount(page) == 1时,才能对该page进行回收,这样会减轻匿名页的回收耗时。

    四 非相机进程内存抑制方面的设想

    上面无论是Android的per_app_memcg,还是app compact优化思路都是在重在做内存回收。

    但是相机场景下,很容易存在后台高优先级进程异常活跃,lmkd不敢轻易杀,但是app compact如果对其做内存回收的话,会存在回收完后,就被这些活跃进程重新以page refault形式加载到内存里面,继续消耗内存,这样的话,app compact工作的确会打折扣。

    完美的方法是对后台这些进程做内存分配抑制。这牵扯到要对系统做Qos管理,相机场景下,如果系统可用内存很少的话,优先满足相机相关进程的内存分配需求。

    最近的一些设想思路:

    借鉴kernel memcg 里面如何对进程进行内存抑制的,然后在系统为用户态进程分配物理内存的必经路径:缺页异常里面判断如果不是相机相关进程的话,并且系统可用内存很少的情况下,就主动抑制该用户态进程的内存分配需求。

    该抑制肯定是弹性的,要结合手机的业务需求来的,比如:

    用户在操作相机拍照,与此同时后台有进程在播放音乐。

    这种场景下,要优先照顾相机性能,听音乐需求倒是其次的,所以在提前感知相机分配内存困难,会影响到相机性能的时候,要抑制住音乐播放进程的内存分配需求。如果相机拍完照,系统内存不再有压力,则立刻解除音乐播放进程的内存分配限制。

    因此该抑制也肯定得是策略和机制相互分离的,内核提供内存抑制接口,用户态Android业务层做策略配置,决定什么抑制开始和结束。

     

     

    展开全文
  • 手机内存优化文章

    2010-10-12 15:57:46
    转载... 本文版权归原作者,中国JAVA手机网收录本文的目的是让更多人阅读到此文章。转载请注明出处为中国JAVA手机网<...gt; ...今天在CSDN上浏览帖子,忽然发现一篇询问内存溢出的解决办法的...

    转载http://www.cnjm.net/tech/article4693.html

    本文版权归原作者,中国JAVA手机网收录本文的目的是让更多人阅读到此文章。转载请注明出处为中国JAVA手机网<www.cnjm.net>

    来自:http://www.cnjm.net/tech/article4693.html


    今天在CSDN上浏览帖子,忽然发现一篇询问内存溢出的解决办法的帖子,有感而发写了这篇文。
          我想做过J2ME的人,特别是像我这样做手机游戏的,肯定会对OutOfMemoryError这个异常深恶痛绝,尤其是在老40这样变态的机型上,甚至 对这个异常都产生了恐惧。还好我现在总算不做这个机型了,对那些仍然在为这个机型移植游戏的同志们感到同情。为了能够稍微缓解一下他们的痛苦,也为了广大 J2ME的从业者和爱好者能尽量减少与该异常的见面次数,CoCoMo将把自己的经验分享一下。
         
      首先了解一下分析内存占用的方法,一般有两种:模拟器自带工具和Runtime类方法。

    JAVA手机网[www.cnjm.net]

     
      模拟器自带工具:WTK貌似带了一个Memory Monitor,而且许多学者人士也夸夸其谈他的使用方法,但我不知道有多少人真正在用。就我对他的了解,首先运行他你的程序会慢的一塌糊涂,这对游戏开 发者来说简直是无法忍受的。但我出于研究目的仍然让他跑了半个小时才发现原来他根本无法显示正确的内存占用量,我载入一张很大的图片后他的内存线好像只出 现了微微的波动又停留在原位,呵,看来的确是拿出来秀的。我一般使用的是7210模拟器自带的内存监视器,模拟的很准,但唯一的缺点是内存太少,才 200K。我也见某些人使用3220的模拟器监视内存,好像内存稍微大一点,我还没来得及尝试就再也不用为老40写程序了,庆幸。

    JAVA手机网[www.cnjm.net]

      Runtime类方法:我经常用这个语句System.out.println(Runtime.getRuntime().freeMemory());后来集成进了我的引擎,他能够显示当前剩余内存。不记得有多少次我用它在老40上来寻找内存占用峰值。
         
          了解了分析内存的方法,来看看内存占用的罪魁祸首:程序和资源。
          程序:类会被编译成class字节码文件随MIDlet的启动加载进内存,而且是一次性全部加入。也就是说MIDlet里类个数越多、单个类程序越长、类 内字符串常量及数据越多,编译后的class文件就越大,载入后占用的内存也越多。我经常在MIDlet类的构造函数里用Runtime方法来查看 MIDlet启动后整个程序占用内存量。
          优化方法:
          1.某些同志将MIDlet程序写成两个类来减少内存占用量,但是以牺牲Java的OOP特性为代价的。在程序比较大时这种弊端将尤为显见。而且 CoCoMo曾经遇到过单个类过大,载入时间过长而违反百宝箱有关Logo 6秒时间限制的情形。因而我现在的程序加带引擎一般都是6-7个类。
          2.尽量编写优雅的代码,减少函数数量,在程序发布时去掉try catch,最大限度的减少程序行数,这一般都是在老40上没有办法的办法,现在CoCoMo已经不靠这个来省内存了。
          3.将数据及字符串写进文件,在用时方载入内存,不用时设为null。
          4.I/O操作getClass().getResourceAsStream(file);、数据库操做 RecordStore.openRecordStore(name, true);、声音创建Manager.createPlayer();、图像创建Image.createImage(file);会在短时间内占用大 量内存且过后释放,如果MIDlet程序内存剩余量不足则会在这些函数频繁调用时发生内存溢出,产生所谓的内存峰值,尤其在老40上比较普遍。

    JAVA手机网[www.cnjm.net]

    当你再次与讨厌的OutOfMemoryError碰面时,多用Runtime查找内存峰值发生位置并尽量将这些语句分开调用,并灵活运用System.gc()来及时回收。

    JAVA手机网[www.cnjm.net]

         资源:
            图片:是占用内存的大户,尤其是手机游戏图片资源众多。对图片资源在内存中占用量的计算成为J2ME游戏开发者的经常性工作,CoCoMo来解释一下如何计算图片在内存中的占用量:
          内存占用量=宽*高*像素字节数,其中像素字节数因机型而异。
          例如一张64*64的图片在7210上的内存占用量=64*64*1.5=6144(字节)=6K、在S60上的内存占用 量=64*64*2=8192(字节)=8K。像素字节数因机型而异,例如7210是4096色机型,也就是说用12位来表示一个像素,所以乘上1.5, 而S60是65536色的机型,用16位来表示一个像素,所以乘上2。
          优化方法:
          有些人认为压缩图片可以节省内存,这种想法是错误的。根据上面的解释图片载入内存后只和宽高有关系,和图片数据量大小没有任何关系,压缩图片只能减少jar大小而不能减少内存占用量。
          1.静态法:减小图片大小,宽高小了结果当然小了。根据这个思路出现了动画编辑器之类的工具,像gameloft的波斯王子,人物被分割后使人体的部位可以重用,各部位紧凑放置都是为了较少图片大小,充分利用图片中的每一寸空间。
          2.动态法:减少同一时刻载入内存的图片数。CoCoMo曾经在火影武士项目中遇到过这种情况,当时有6种怪物,如果同时载入内存在老40上肯定爆掉了, 但是每关只出现两到三种怪物,所以每一关只需要载入该关出现的怪物图片即可。现在想起来当时做这个项目在老40上溢出频出,真把我搞死了。
          声音:声音也是比较耗用内存的资源,声音中音轨所占的byte会转化成字节流被载入到内存中。因而减少音轨所占byte即可减少内存耗用量。目前 gameloft的做法是用声音转化工具将mid转化为ott,然后变为ByteArrayInputStream字节流来创建Player

     

     

     

     

    一.代码优化

      内存会溢出肯定和代码逃不了关系,99.99%学java的人都知道垃圾回收器是java的一大优点并据此来嘲笑C++。显然这个特性为代码编写者省了不少事,但这个特性却带来了不少隐患。举个例子在游戏当中经常有不同场景的切换,如从游戏逻辑退到主菜单逻辑,对游戏逻辑对象的态度很多人会选择忘记等待垃圾回收器来收尸。乍看之下似乎并无不妥垃圾回收器会来善后。实际上垃圾回收器并非实时的,它不像C++的Delete语句马上释放不用的内存。当从游戏逻辑切换到主菜单逻辑这时两个对象同时存在很可能这时内存就不够用了。读到这里很多人会发现实际上垃圾回收器在j2me上并不怎么好用,从一个角度上来讲在j2me上所有垃圾必须由手工释放,除简单类型以外所有对象都必须显式地置空例如imgs=null;实际上java提供了一个不错的工具用 来查找内存溢出,java.lang.Runtime.freeMemory()。它可以返回当前的剩余内存数,将它适当的安放在代码中可以有效的监测内存使用状况。很大一部份的j2me程序员之前都是从事pc软件开发工作,充裕的内存掩盖了许多写代码的不良习惯。如下所示:

      //a不为空

      a=newLogic();

      很多人可能对此有异议,他们会认为新的对象会把旧的对象冲掉并且释放内存。这里面包含两个问题:1.该段代码是先创建对象然后再进行赋值操作的,也就是说在这期间有两个对象同时存在这就很可能会产生溢出。2.这样做也会妨碍垃圾回收器的工作

      较好的写法如下:

      a=null;

      a=newLogic();

      虽然麻烦了点但在j2me中还是必要的。接着看下例。

      drawString("游戏时间:"+time,50,50,Graphics.LEFT|Graphics.TOP);

      "游戏时间:"+time很完美在paint()方法当中每次都被刷一遍显示在屏幕上。危机往往隐藏在美丽的外表,该语句会引起新的内存重新分配来存储"游戏时间:"+time而显示完以后又必须由垃圾回收器释放,用了双倍时间,并且容易发生内存溢出。依此类推在重复执行的方法里应尽量避免重复定义对象。与paint()方法类似在循环里也有类似的情况存在。

      把所有对象的初始化放在构造函数里想必是再正当不过了,大多数人通常的做法是把当前逻辑所要用到的资源通通初始化完毕。

      很大一部份的内存溢出都是发生在构造函数中。内存使用的高峰期都是在构造函数中所以避开这个高峰能有效的防止溢出。建议最好的办法是第一次使用时初始化。如下所示

      if(img==null){

      //初始化

      }

      现在做游戏很多时候都需要地图数组,声音数组,还有一些其它资源这些资源很多可以放在代码中也有的可以放在文件当中。

      强烈建议将这些资源放在文件中需要时在load进来。这些资源文件如果放在代码中则会占用不小的代码段空间,而代码一般是程序一运行就装载到内存当中。

      除上面列举的方法外还有一些大家所熟知的顺便一提,比如关闭没用的rms,关闭没用的网络连接,关闭没用的流。正确地停止线程。良好的程序架构减少代码偶合性也是一个不错的方法,无论在代码调式,内存释放都可以做到非常清析。

     

     

    二.图片优化

      j2me的内存杀手无疑非图片莫属,一张3k的图片可以占用20多k的内存不信大家把load前后的内存剩余打印出来对比看看。所以防止内存溢出最直接的办法就是从图片入手。

      1.图片压缩:多数人马上会想到这个办法。不错这个办法是最有效的。在photoshop里图片制作完成后不要选择"存储为",而是选择"存储为web所用格式"可以根据里面的选项进行压缩,特别是颜色这一项越小越好不过相应的图像会有所失真。不要认为这样就完了。

      实际上该图片还可以再次压缩,在网上有许多类似的工具。推荐一款可以压缩png格式的软件xat.comImageOptimizer效果不错。经常都有70%的压缩率且图像不会失真。

      假如你有多张规格一样的图片,那么建议你把它做成一张长条图片。有两个原因:

      1、这样节省存储空间和内存空间。大家可做个试验将10张图片的内容放在一张当中对比看看文件大小有没有变化。

      2、10张图片需要10个image对象需要进行10次io操作浪费时间不说还浪费内存。当笔者发现这个好处时兴奋地把所有图片都存成一张,吱地一声内存又溢出了...原因想必大家也知道!!图片太大了不要把不同界面的图片整合在一起否则经常会得不偿失。

      作图时还有一些细节需要注意,颜色数量,分辩率,图像模式(最好是索引颜色),画布大小都会影响到图片大小。

     

     

    三.工具优化

      谁都知道混淆器是用来保护代码的以加大反编译的难度(个人认为这是在嘲笑程序员的智商)。实际上用它来优化程序也是不错的选择,至少有两点好处:

      1、压缩程序大小。一个60k的程序经常可以压掉10k左右。10k的空间对于写低端手机的程序员简直是雪中送碳,多少超过64k限制的游戏都受过它的恩惠;

      2、节省内存空间。用脚去想也想得出来代码少了内存里的代码段自然就短了。

      .使用Profiler去查找哪里需要优化!
     
      .记住Profiler不代表真机上的优化结果,使用System Timer来在真机上做最后的测试!

      .在做低级优化之前,总是要先思考算法是否是最优!

    J

      .尽可能的使用SetClip()来减少绘图区域,相对于SetClip(),drawImage()所花的时间会更可观!

      .尽最大可能的进行对需要的数据进行预先计算并将结果保存在缓冲里!

      .String类很容易产生垃圾内存,尽可能的使用StringBuffer代替String或用final static来定义之!!

    J

      .尽可能的使用<<和>>来代替*和/!

      .使用位操作来代替%运算!

      .与0比较比与其他数值比较快!

      .局部变量比其他类型的变量运算要快!
      
      .尽量使用已有算法!

     

     

    3.让多个对象使用同样的监听器,比如让主MIDlet类实现CommandListener和ItemStateListener接口。

    4.把所有资源文件做成一个数据文件。

    5.class中生成尽可能少的方法。速度比较:同步方法<接口方法<实例方法<final方法<静态方法

    展开全文
  • 手机游戏内存优化

    2015-01-22 15:22:41
    内存优化是手机游戏开发者们极为关注的事情,手机内存技术更是短短几年间就走完了PC端花了几十年才走完的路。安卓手机3G RAM已经是中高端旗舰的标配,而传闻4G RAM的手机也即将在今年大规模上市。安卓基于Linux的...


     

    内存优化是手机游戏开发者们极为关注的事情,手机内存技术更是短短几年间就走完了PC端花了几十年才走完的路。安卓手机3G RAM已经是中高端旗舰的标配,而传闻4G RAM的手机也即将在今年大规模上市。安卓基于Linux的内存管理机制决定了手游研发过程必然需要针对内存管理做大量的工作,今天我们就来聊聊内存那些事儿。


    一般情况下,Linux的内存机制是无论你的手机内存多大,都会通过读取大量硬盘数据去充分利用你的内存以保证系统能够拥有更好的性能。


    安卓系统继承了Linux这一内存机制,并且通过一定的规律去实现内存回收,系统在进行内存调度时有个阀值,只有当系统可用内存低于这个值时,系统才根据一定规律去关闭用户不需要的东西。这个值默认设置得很小,所以通常用户会发现安卓手机虽然开几个应用就会把内存占用到70%~80%,但随后无论你怎样开启应用手机也不会卡。这就是因为当你在开启新应用的同时系统也在关掉一些老应用。


    虽然安卓有着良好的回收机制,但仍然有可能出现应用相互抢占内存导致内存溢出的现象,这是因为我们的手机在平时的使用场景往往非常复杂,安卓手机用户往往都会安装QQ、微信等长期驻留在内存空间的应用。假如在运行游戏的时候后台运行QQ、微信一类的应用,时长有可能会发生游戏卡顿、崩溃等现象。这样的现象在内存较小的中低端设备上尤为突出。那么游戏通常在什么情况下容易发生内存吃紧呢?


    我们来看看具体事例,这是征途手游版在TestBird手游自动化云测试平台上的一组内存占用曲线,测试机型是HTC Desire816


     

    我们注意到游戏进行到第11分钟的时候,出现了内存突然剧增的现象。内存占用从80M左右暴增到181M,因此我们需要看看在这个时候游戏发生了些什么。


    拖动时间轴到内存暴增之前,我们发现在内存占用较低的时候游戏是处在启动和登录状态,在创建角色的时候内存占用量开始增加,当人物创建完毕以后,进入游戏场景,此时游戏引擎会要求CPU读取大量数据到RAM当中,这样就导致内存占用会非常大。


     

    同样的情况,通常在场景中出现大量单位或技能特效对CPU产生非常复杂的运算需求时,也很容易导致内存占用瞬间暴增。这样的情况就会容易导致内存溢出等现象,而作为开发者,一定要尽可能在游戏上线前通过大量的对自身游戏进行测试和优化,尽可能避免游戏的过程中出现内存溢出的现象。


     

                                                                                                                                                   文章来源:TestBird手游测试研究中心

    展开全文
  • 这里针对上图进行简单描述:1)物理地址与虚拟地址:虚拟内存是程序和物理内存之间引入的中间层,目的是解决直接使用物理内存带来的安全性问题、超过物理内存大小需求无法满足等等问题。而Linux的内存管理就是建立在...
  • 手机APP内存资源优化方法研究
  • 内存优化.jar java手机游戏--内存优化.jar java手机游戏--内存优化.jarjava 手机游戏--内存优化.jar
  • 内存优化

    2019-06-06 13:48:46
    内存优化主要是处理内存泄漏 (这个在面试时百分百问到,至少我最近一直被问) 内存泄漏与手机卡顿 因为有内存泄漏,所以内存被占用越来越多,那么GC会更容易被触发,GC会越来越频发,但是当GC的时候所有的线程...
  • 手机性能的优化内存优化和运行效率的优化 1.内存优化  说内存优化,首先要知道有什么东西会占据程序的内存,可优化的主要是两部分:数据和资源,先说数据,做短连接的游戏客户端有两种处理数据的...
  • 内存优化神器

    2017-02-13 23:52:37
    一款优化手机内存的神器。
  • 图片相关的内存优化 图片相关的优化主要涉及几个方面:分辨率的适配、图片的压缩、图片的缓存 分辨率的适配 分辨率的适配主是针对我们放在drawable目录下的图片资源,目录和对应的手机分辨率可以看下图 ...
  • 很强的,用了就知道,你可以获取额外空闲的java内存哦!还等什么快下一个吧
  • 安卓内存优化

    千次阅读 2020-05-11 18:12:39
    文章目录为什么要进行内存优化优化步骤1,查看当前的应用内存使用情况,发现是否有内存泄漏2,内存泄漏点查找,并解决问题3,其它一些日常项目中编码的注意点 为什么要进行内存优化 安卓应用如果不进行内存优化,...
  • 在写今天这篇文章前,我又翻了翻三年前我在 WeMobileDev 公众号写过的《Android 内存优化杂谈》,今天再看,对里面的一句话更有感触:“我们并不能将内存优化中用到的所有技巧都一一说明,而且随着 Android 版本的...
  • Android不同于Linux,有一套自己独特的进程管理模块,这个模块有更强的可定制性,可根据“oom_adj”值的范围来决定进程管理策略,比如可以设定“当内存小于X时,结束“oom_adj”大于Y的进程”。 这给了进程管理脚本...
  • 一键优化的特别版,可以高效、快速、准确的清理手机垃圾,卸载手机不常用的软件,提升手机运行速度,降低CPU占用率,是一个精巧的手机优化工具。
  • Android手机内存管理与性能优化&JNI、NDK高级编程(JNI、Dalvik、内存监测) 课程分类:Android 适合人群:中级 课时数量:34小节课时 用到技术:Dalvik,DDMS,File Explorer,Adapter和图片处理,查询数据库和Static...
  • 内存优化01

    2017-07-25 11:47:02
    内存优化01前言为什么内存优化?在一个商业项目中,很有可能因为工程师的疏忽,导致代码质量不佳,影响到程序的运行效率,从而让用户感知到应用的卡顿、崩溃。而Android开发中,每个Android应用在手机上申请的内存...
  • android内存优化

    2016-09-02 11:53:37
    android内存优化总结  在写app的时候,越是到后面越是要考虑到app实际效率和体验的问题。往往在写个人的项目时,这方面考虑的比较少,因为用户少,而且... 为什么需要内存优化 现在的手机配置都不一样,在开发app的
  • 一、为什么要进行内存优化 1.每个APP运行内存都有限制,运行内存不够时导致APP崩溃 2.当APP所占用的内存快占满手机内存时,影响APP的性能,例如流畅度和响应速度//下面我们来看看看如何获取手机的内存和最大内存 ...
  • Android内存优化

    2019-05-09 14:34:33
    随着Android生态的多年发展,现在4GB 内存手机都变成了主流,2008 年的手机只有可怜的 140MB 内存,而今年的华为Mate 20 Pro 手机内存已经达到了 8GB,在以前低内存设备更容易出现内存不足引起的异常和卡顿,...
  • 内存优化可以使用Unity profiler或者对应平台的内存分析工具进行分析,从 Unity3D内部的内存+Mono的托管内存 进行分析,避免内存泄漏。 1、内存测试一般使用手机测试,Unity profiler在Editor测试下的结果不准确。...
  • android 内存优化

    2017-08-22 11:25:46
    1为什么要进行内存优化 app运行内存限制,oom导致app崩溃 app性能:流程性,响应速度和用户体验 android 内存管理方式 1,获取当前手机单进程最大内存 ActivityManager manager = ...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 1,558
精华内容 623
关键字:

手机内存优化