OOM

oom(out of memory)
Android系统对 dalvik 的 vm heapsize 作了硬性限制,当 java 进程申请的 java 空间超过阈值时,就会抛出OOM异常。可以通过adb shell getprop 或者 getprop dalvik.vm.heapgrowthlimit 查看此阈值。

getprop dalvik.vm.heapgrowthlimit
256m
PRODUCT_PROPERTY_OVERRIDES += \dalvik.vm.heapstartsize=8m \dalvik.vm.heapgrowthlimit=64m \dalvik.vm.heapsize=256m \dalvik.vm.heaptargetutilization=0.75 \dalvik.vm.heapminfree=512k \dalvik.vm.heapmaxfree=8m

我们用下面的代码获取heapsize:

ActivityManager manager = (ActivityManager)getSystemService(Context.ACTIVITY_SERVICE);
int heapSize = manager.getMemoryClass();
int maxHeapSize = manager.getLargeMemoryClass();  // manafest.xml   android:largeHeap="true"

程序发生 OMM 并不表示 RAM 不足,而是因为程序申请的 java heap 对象超过了 dalvik vm heapgrowthlimit。也就是说,在 RAM 充足的情况下,也可能发生 OOM(这句话比较关键)

这样设计的目的是为了让 Android 系统能同时让比较多的进程常驻内存,这样程序启动时就不用每次都重新加载到内存,能够给用户更快的响应。迫使每个应用程序使用较小的内存,移动设备非常有限的RAM就能使比较多的app常驻其中。

进程的内存空间只是虚拟内存(或者叫作逻辑内存),而程序的运行需要的是实实在在的内存,即物理内存(RAM)。在必要时,操作系统会将程序运行中申请的内存(虚拟内存)映射到RAM,让进程能够使用物理内存。

以下两个概率区分Dalvik Heap 和Native Heap

native进程:采用C/C++实现,不包含dalvik实例的linux进程,/system/bin/目录下面的程序文件运行后都是以native进程形式存在的

java进程:实例化了 dalvik 虚拟机实例的 linux 进程,进程的入口 main 函数为 java 函数

130|console:/ # dumpsys meminfo com.funshion.ottedu
Applications Memory Usage (in Kilobytes):
Uptime: 9506057 Realtime: 9506057** MEMINFO in pid 1187 [com.funshion.ottedu] **Pss  Private  Private     Swap      Rss     Heap     Heap     HeapTotal    Dirty    Clean    Dirty    Total     Size    Alloc     Free------   ------   ------   ------   ------   ------   ------   ------Native Heap    33913    33848        0        0    35692    48244    34778     2811Dalvik Heap     4135     3960        0        0     7220    12945     6473     6472Dalvik Other     2468     1940        0        0     3288                           Stack     1012     1012        0        0     1016                           Ashmem        2        0        0        0        8                           Other dev      104        0      104        0      324                           .so mmap     5411      204      300        0    41620                           .jar mmap     2859        0       40        0    28928                           .apk mmap     6124      180     2520        0    11280                           .ttf mmap       92        0        0        0      312                           .dex mmap    14715       20    14608        0    14960                           .oat mmap       75        0        0        0     2148                           .art mmap     6362     6052        0        0    14632                           Other mmap     2098       40      924        0     5184                           GL mtrack    37872    37872        0        0    37872                           Unknown     3686     3684        0        0     3920                           TOTAL   120928    88812    18496        0   120928    61189    41251     9283

C/C++ 申请的内存空间在 native heap 中,而 java 申请的内存空间则在 dalvik heap中

Java 程序发生 OMM 并不是表示 RAM 不足。
如果 RAM 真的不足,会发生什么呢?这时 Android 的 memory killer 会起作用(OOMKiller),当 RAM 所剩不多时,memory killer 会杀死一些优先级比较低的进程来释放物理内存,让高优先级程序得到更多的内存。

参考链接:Android 中的 Dalvik Heap 和 Native Heap


OOMKiller

OOMKiller 是Linux Kernel 的内存监控机制。会在内存紧张的时候,会依次kill内存占用较高的进程,并在/var/log/message中进行记录。里面会记录一些如pid,process name,cpu mask,trace等信息,通过监控可以发现类似问题。

Mar 10 23:40:13 ...... kernel: xxx invoked oom-killer: gfp_mask=0x201da, order=0, oom_score_adj=0
Mar 10 23:40:13 ...... kernel: [<ffffffff811171b1>] oom_kill_process+0x241/0x390
Mar 10 23:40:13 ...... kernel: [<ffffffff81116c4d>] ? oom_unkillable_task+0xcd/0x120
Mar 10 23:40:13 ...... kernel: [ pid ]   uid  tgid total_vm rss nr_ptes swapents oom_score_adj

在我们底层通过Malloc 申请内存,根据Linux有一种内存机制,会返回一个非空的内存,但是Linux并不保证这些内存马上可用(虚拟内存->物理内存),当你需要使用的物理内存不足时,也会出发OOM Killer.
内核代码为:common/mm/oom_kill.c.
其调用栈为:

malloc()_alloc_pages()out_of_memory() select_bad_process()oom_kill_process()
common/mm/oom_kill.c
int sysctl_panic_on_oom;//内存不够时内核是否直接panic
int sysctl_oom_kill_allocating_task;//是否选择当前正在申请内存的进程进行kill
bool out_of_memory(struct oom_control *oc)
{...constraint = constrained_alloc(oc);if (constraint != CONSTRAINT_MEMORY_POLICY)oc->nodemask = NULL;check_panic_on_oom(oc, constraint);if (!is_memcg_oom(oc) && sysctl_oom_kill_allocating_task &&current->mm && !oom_unkillable_task(current, NULL, oc->nodemask) &&current->signal->oom_score_adj != OOM_SCORE_ADJ_MIN) {get_task_struct(current);oc->chosen = current;oom_kill_process(oc, "Out of memory (oom_kill_allocating_task)");return true;}select_bad_process(oc);if (oc->chosen && oc->chosen != (void *)-1UL) {oom_kill_process(oc, !is_memcg_oom(oc) ? "Out of memory" :"Memory cgroup out of memory");schedule_timeout_killable(1);}...
}

Kill 策略:

1)计算该进程以及其子进程所占用的内存;
2)计算CPU时间和存活时间
3)计算进程优先级等/proc/pids/oom_score,/proc/pids/oom_adj;
占用内存越高,得分越高,cpu时间和存活时间越高,得分越低;进程优先级越高,得分越低
综合上述因素后,会得到一个point,得分最高的会被选中,然后被kill掉

LMK

基于 Linux Kernel 的 OOMKiller 思想,Android 系统拓展出了自己的内存监控体系 : Low Memory Killer.相比 Linux 达到临界值才触发,Android 实现了不同梯级的 Killer。

横向比较oomkiller Vs lmk:
OOMkiller(Out Of Memory Killer)是在Linux系统无法分配新内存的时候,选择性杀掉进程,到oom的时候,系统可能已经不太稳定,而LowMemoryKiller是一种根据内存阈值级别触发的内存回收的机制,在系统可用内存较低时,就会选择性杀死进程的策略,相对OOMKiller,更加灵活。

先来看看一下两个文件节点:

/sys/module/lowmemorykiller/parameters/adj
/sys/module/lowmemorykiller/parameters/minfree

我们可以修改以上两个文件节点的值:

write /sys/module/lowmemorykiller/parameters/adj        0, 8
write /sys/module/lowmemorykiller/parameters/minfree 1024, 4096

取值说明:当系统可用内存低于4096个pages时,则会杀掉oom_score_adj>=8的进程

ActivityManagerService 在运行时会根据当前的系统配置自动调整 adj 和 minfree.
AMS.updateOomLevels 方法也是通过修改上面两个文件来实现的。

console:/ # dumpsys activity oOOM levels:-900: SYSTEM_ADJ (   73,728K)-800: PERSISTENT_PROC_ADJ (   73,728K)-700: PERSISTENT_SERVICE_ADJ (   73,728K)0: FOREGROUND_APP_ADJ (   73,728K)100: VISIBLE_APP_ADJ (   92,160K)200: PERCEPTIBLE_APP_ADJ (  110,592K)250: PERCEPTIBLE_LOW_APP_ADJ (  129,024K)300: BACKUP_APP_ADJ (  147,456K)400: HEAVY_WEIGHT_APP_ADJ (  147,456K)500: SERVICE_ADJ (  147,456K)600: HOME_APP_ADJ (  147,456K)700: PREVIOUS_APP_ADJ (  147,456K)800: SERVICE_B_ADJ (  147,456K)900: CACHED_APP_MIN_ADJ (  147,456K)999: CACHED_APP_MAX_ADJ (  184,320K)Process OOM control (8 total, non-act at 0, non-svc at 0):PERS # 7: sys    F/ /PER  LCM  t: 0 511:system/1000 (fixed)oom: max=-900 curRaw=-900 setRaw=-900 cur=-900 set=-900state: cur=PER  set=PER  lastPss=0.00 lastSwapPss=0.00 lastCachedPss=0.00cached=false empty=false hasAboveClient=falsePERS # 5: pers   F/ /PER  LCM  t: 0 683:com.android.systemui/u0a39 (fixed)oom: max=-800 curRaw=-800 setRaw=-800 cur=-800 set=-800state: cur=PER  set=PER  lastPss=0.00 lastSwapPss=0.00 lastCachedPss=0.00cached=false empty=false hasAboveClient=falsePERS # 4: pers   F/ /PER  LCM  t: 0 785:com.droidlogic/1000 (fixed)oom: max=-800 curRaw=-800 setRaw=-800 cur=-800 set=-800state: cur=PER  set=PER  lastPss=0.00 lastSwapPss=0.00 lastCachedPss=0.00cached=false empty=false hasAboveClient=falsePERS # 3: pers   F/ /PER  LCM  t: 0 872:com.android.se/1068 (fixed)oom: max=-800 curRaw=-800 setRaw=-800 cur=-800 set=-800state: cur=PER  set=PER  lastPss=0.00 lastSwapPss=0.00 lastCachedPss=0.00cached=false empty=false hasAboveClient=falsePERS # 2: pers   F/ /PER  LCM  t: 0 851:com.android.networkstack.process/1073 (fixed)oom: max=-800 curRaw=-800 setRaw=-800 cur=-800 set=-800state: cur=PER  set=PER  lastPss=0.00 lastSwapPss=0.00 lastCachedPss=0.00cached=false empty=false hasAboveClient=falseProc # 6: psvc   F/ /PER  LCM  t: 0 664:com.android.bluetooth/1002 (service)com.android.bluetooth/.btservice.AdapterService<=Proc{511:system/1000}oom: max=1001 curRaw=-700 setRaw=-700 cur=-700 set=-700state: cur=PER  set=PER  lastPss=0.00 lastSwapPss=0.00 lastCachedPss=0.00cached=false empty=true hasAboveClient=falseProc # 1: fg     T/A/TOP  LCM  t: 0 897:com.android.tv.settings/1000 (top-activity)oom: max=1001 curRaw=0 setRaw=0 cur=0 set=0state: cur=TOP  set=TOP  lastPss=0.00 lastSwapPss=0.00 lastCachedPss=0.00cached=false empty=false hasAboveClient=falseProc # 0: vis    F/ /BFGS ---  t: 0 922:android.ext.services/u0a43 (service)android.ext.services/.autofill.InlineSuggestionRenderServiceImpl<=Proc{511:system/1000}oom: max=1001 curRaw=100 setRaw=100 cur=100 set=100state: cur=BFGS set=BFGS lastPss=0.00 lastSwapPss=0.00 lastCachedPss=0.00cached=false empty=true hasAboveClient=falsemHomeProcess: ProcessRecord{74a3758 897:com.android.tv.settings/1000}mPreviousProcess: null
console:/ #
ADJ优先级 优先级 优先级
UNKNOWN_ADJ 16 一般指将要会缓存进程,无法获取确定值
CACHED_APP_MAX_ADJ 15 不可见进程的adj最大值(不可见进程可能在任何时候被杀死)
CACHED_APP_MIN_ADJ 9 不可见进程的adj最小值(不可见进程可能在任何时候被杀死)
SERVICE_B_AD 8 B List中的Service(较老的、使用可能性更小)
PREVIOUS_APP_ADJ 7 上一个App的进程(比如APP_A跳转APP_B,APP_A不可见的时候,A就是属于PREVIOUS_APP_ADJ)
HOME_APP_ADJ 6 Home进程
SERVICE_ADJ 5 服务进程(Service process)
HEAVY_WEIGHT_APP_ADJ 4 后台的重量级进程,system/rootdir/init.rc文件中设置
BACKUP_APP_ADJ 3 备份进程(这个不太了解)
PERCEPTIBLE_APP_ADJ 2 可感知进程,比如后台音乐播放
VISIBLE_APP_ADJ 1 可见进程(可见,但是没能获取焦点,比如新进程仅有一个悬浮Activity,Visible process)
FOREGROUND_APP_ADJ 0 前台进程(正在展示是APP,存在交互界面,Foreground process)
PERSISTENT_SERVICE_ADJ -11 关联着系统或persistent进程
PERSISTENT_PROC_ADJ -12 系统persistent进程,比如telephony
SYSTEM_ADJ -16 系统进程
NATIVE_ADJ -17 native进程(不被系统管理)

从Android 7.0开始,ADJ采用100、200、300;在这之前的版本ADJ采用数字1、2、3,这样的调整可以更进一步地细化进程的优先级,比如在VISIBLE_APP_ADJ(100)与PERCEPTIBLE_APP_ADJ(200)之间,可以有ADJ=101、102级别的进程。

Android应用的优先级是如何更新的
在Android家族,5.0之前的系统进程优先级是AMS进程直接修改的(通过了Linux中的一个proc文件体统),
5.0之后,是修改优先级的操作被封装成了一个独立的服务-lmkd,lmkd服务位于用户空间,其作用层次同AMS、WMS类似,就是一个普通的系统服务(其实就是AMS通过socket向lmkd服务发送请求,让lmkd去更新进程的优先级)

AMS 更新进程优先级:
1)AMS.applyOomAdjLocked(),会设置某个进程的adj;
2)AMS.updateConfiguration(),会更新整个各个级别的oom_adj信息.
3)AMS.cleanUpApplicationRecordLocked()或者handleAppDiedLocked(),会将某个进程从lmkd策略中移除.

lmkd:

system/core/rootdir/init.rcstart lmkd
system/memory/lmkd/lmkd.rc
system/memory/lmkd/lmkd.c
system/memory/lmkd/lmkd.c
//更新优先级
static void cmd_procprio(LMKD_CTRL_PACKET packet) {struct proc *procp;char path[80];char val[20];int soft_limit_mult;struct lmk_procprio params;lmkd_pack_get_procprio(packet, &params);if (params.oomadj < OOM_SCORE_ADJ_MIN ||params.oomadj > OOM_SCORE_ADJ_MAX) {ALOGE("Invalid PROCPRIO oomadj argument %d", params.oomadj);return;}snprintf(path, sizeof(path), "/proc/%d/oom_score_adj", params.pid);snprintf(val, sizeof(val), "%d", params.oomadj);writefilestring(path, val);if (use_inkernel_interface)return;...
}
static int kill_one_process(struct proc* procp, int min_score_adj,enum vmpressure_level level) {...r = kill(pid, SIGKILL);ALOGI("Killing '%s' (%d), uid %d, adj %d\n""   to free %ldkB because system is under %s memory pressure oom_adj %d\n",taskname, pid, uid, procp->oomadj, tasksize * page_k,level_name[level], min_score_adj);pid_remove(pid);...
}

LomemoryKiller 杀死进程(内核)
shrinker
LMK驱动通过注册shrinker来实现的,shrinker是linux kernel标准的回收内存page的机制,由内核线程kswapd负责监控。当内存不足时kswapd线程会遍历一张shrinker链表,并回调已注册的shrinker函数来回收内存page,kswapd还会周期性唤醒来执行内存操作。

common/drivers/staging/android/lowmemorykiller.c

static struct shrinker lowmem_shrinker = {.scan_objects = lowmem_scan,.count_objects = lowmem_count,.seeks = DEFAULT_SEEKS * 16
};static int __init lowmem_init(void) {register_shrinker(&lowmem_shrinker);return 0;
}static void __exit lowmem_exit(void) {unregister_shrinker(&lowmem_shrinker);
}
static unsigned long lowmem_scan(struct shrinker *s, struct shrink_control *sc)
{struct task_struct *tsk;struct task_struct *selected = NULL;unsigned long rem = 0;int tasksize;int i;short min_score_adj = OOM_SCORE_ADJ_MAX + 1;int minfree = 0;int selected_tasksize = 0;short selected_oom_score_adj;int array_size = ARRAY_SIZE(lowmem_adj);//获取当前剩余内存大小int other_free = global_page_state(NR_FREE_PAGES) - totalreserve_pages;int other_file = global_page_state(NR_FILE_PAGES) -global_page_state(NR_SHMEM) -total_swapcache_pages();//获取数组大小if (lowmem_adj_size < array_size)array_size = lowmem_adj_size;if (lowmem_minfree_size < array_size)array_size = lowmem_minfree_size;//遍历lowmem_minfree数组找出相应的最小adj值for (i = 0; i < array_size; i++) {minfree = lowmem_minfree[i];if (other_free < minfree && other_file < minfree) {min_score_adj = lowmem_adj[i];break;}}if (min_score_adj == OOM_SCORE_ADJ_MAX + 1) {return 0;}selected_oom_score_adj = min_score_adj;rcu_read_lock();for_each_process(tsk) {struct task_struct *p;short oom_score_adj;if (tsk->flags & PF_KTHREAD)continue;p = find_lock_task_mm(tsk);if (!p)continue;if (test_tsk_thread_flag(p, TIF_MEMDIE) &&time_before_eq(jiffies, lowmem_deathpending_timeout)) {task_unlock(p);rcu_read_unlock();return 0;}oom_score_adj = p->signal->oom_score_adj;//小于目标adj的进程,则忽略if (oom_score_adj < min_score_adj) {task_unlock(p);continue;}//获取的是进程的Resident Set Size,也就是进程独占内存 + 共享库大小。tasksize = get_mm_rss(p->mm);task_unlock(p);if (tasksize <= 0)continue;//算法关键,选择oom_score_adj最大的进程中,并且rss内存最大的进程.if (selected) {if (oom_score_adj < selected_oom_score_adj)continue;if (oom_score_adj == selected_oom_score_adj &&tasksize <= selected_tasksize)continue;}selected = p;selected_tasksize = tasksize;selected_oom_score_adj = oom_score_adj;lowmem_print(2, "select '%s' (%d), adj %hd, size %d, to kill\n",p->comm, p->pid, oom_score_adj, tasksize);}if (selected) {long cache_size = other_file * (long)(PAGE_SIZE / 1024);long cache_limit = minfree * (long)(PAGE_SIZE / 1024);long free = other_free * (long)(PAGE_SIZE / 1024);//输出kill的loglowmem_print(1, "Killing '%s' (%d), adj %hd,\n" \ ...);lowmem_deathpending_timeout = jiffies + HZ;set_tsk_thread_flag(selected, TIF_MEMDIE);//向选中的目标进程发送signal 9来杀掉目标进程send_sig(SIGKILL, selected, 0);rem += selected_tasksize;}rcu_read_unlock();return rem;
}

参考:
LowMemoryKiller原理
Android进程优先级ADJ算法
Android LowMemoryKiller原理分析
Android 内存管理之LowMemoryKiller实现原理分析

Android OOM、OOMKillery以及LMK相关概念相关推荐

  1. android oom 全解析

    Android oom 有时出现很频繁,这一般不是Android设计的问题,一般是我们的问题. 就我的经验而言,出现oom,无非主要是以下几个方面: 一.加载对象过大 二.相应资源过多,没有来不及释放 ...

  2. Android 系统性能优化(43)---Android OOM案例分析

    Android OOM案例分析 在Android(Java)开发中,基本都会遇到java.lang.OutOfMemoryError(本文简称OOM),这种错误解决起来相对于一般的Exception或 ...

  3. 关于 android oom(内存溢出的分析)

    laozhu1124 android oom 全解析 Android oom 有时出现很频繁,这一般不是Android设计的问题,一般是我们的问题. 就我的经验而言,出现oom,无非主要是以下几个方面 ...

  4. android p版本 字符串常量池,Android OOM 问题

    1. java 虚拟机模型 image.png 2. 关于Android heapsize 的位置 取自android10-c2f2-release \frameworks\base\core\jni ...

  5. android oom 检测工具,Android中UI检测、内存泄露、OOM、等优化处理

    对Android整个优化分析,非常棒,下面是自己的积累笔记.可直接看原文. 界面检测工具: Fps: GPU检测, 使用Lint进行资源及冗余UI布局等优化 (很强大,布局的冗余) Memory检测G ...

  6. Android OOM案例分析

    在Android(Java)开发中,基本都会遇到java.lang.OutOfMemoryError(本文简称OOM),这种错误解决起来相对于一般的Exception或者Error都要难一些,主要是由 ...

  7. android oom工具,Android OOM-Heap,MAT工具检测内存泄露

    概述 在android的开发中,要时刻主要内存的分配和垃圾回收,因为系统为每一个dalvik虚拟机分配的内存是有限的,在google的G1中,分配的最大堆大小只有16M,后来的机器一般都为24M,实在 ...

  8. Android OOM出现常见原因及解决办法

    手机在运行应用的时候,一直在崩溃,而这个异常就是OutOfMemory的错误,简称为OOM, 搞得我们也是极其的崩溃,最后 ,我们是通过网上搜集资料和代码走查的方式来优化解决的,这里,我就把我们收集到 ...

  9. Android oom pthread_create (1040KB stack)分析及解决

    首先看一下错误信息如下 java.lang.OutOfMemoryError: pthread_create (1040KB stack) failed: Out of memory     at j ...

最新文章

  1. 在Leangoo中,如何快速切换项目内看板?
  2. 年末裁员事件背后的启示录(1)
  3. Leaflet中自定义marker的icon图标
  4. 小甲鱼 OllyDbg 教程系列 (六) :PJ 软件功能限制(不修改jnz的非爆破方法)
  5. 性能测试过程中oracle数据库报ORA-27301 ORA-27302错
  6. 摄像头分辨率怎么调整_网络监控摄像头怎么选择 网络监控摄像头选择方法【介绍】...
  7. (80)Vivado综合约束方法
  8. 连接串口_气压计PTB330连接HMI串口屏
  9. JDBC秒变C3P0连接池——再加连接解耦
  10. MQ(队列消息的入门)
  11. Linux之netstat命令详解
  12. Ubuntu下安装qt57creator-plugin-ros,在QT中进行ROS开发(亲测有效)
  13. oracle游标作为out参数,oracle 存储过程 带游标作为OUT参数输出
  14. 省市区县街道四级联动下拉菜单
  15. 用c语言反向输出5ge字符,C语言入门:05.scanf函数
  16. 惠普打印机驱动下载(电脑系统和打印机型号自动匹配)
  17. 密码库LibTomCrypt学习记录——(2.25)分组密码算法的工作模式——EAX加密认证模式
  18. 外包:.epub格式漫画解压后图片顺序重排
  19. UrlRewrite
  20. 计算机usb共享网络泄密,信息泄密的三种渠道

热门文章

  1. 大数据行业发展进步的原动力是什么?
  2. 兄弟情谊 bromance
  3. 【从零开始学c++】——基础语法的详细讲解
  4. 随机森林算法-Deep Dive
  5. [Game Framework之StarForce解读]01.StarForce下载运行
  6. StarForce学习1-流程
  7. C++ 课本习题(程序设计题)
  8. 高转速滑环的特点,应用与发展趋势
  9. 我公司有个华为离职的同事来我公司担任人工智能芯片研发
  10. Blender骨骼动画快速教程