巧妇能为少米之炊(2)——幽灵侩子手(LMK)

LMKD浅析(四)——Android Q新特性(QCOM篇) 前言: 在lmkd这个文件上,除了MTK,QCOM也大刀阔斧的在改,导致这两家的基线和AOSP都有一定差异,本篇着重讨论QCOM平台的一些差异的地方,AOSP的晚点再说吧…… 关于MTK平台的介绍,可以移步这里; ... 阅读详情

使用安卓的朋友可能会遇到过这样的问题,就是某个应用正在使用,突然它被关闭了,或者打开某个应用,然后它就退出了,其实这背后就是LMK(Low Memory Killer)在起作用,所有形象地称之为幽灵刽子手。


1.版本平台

2.概念

3.核心结构及调用

4.总结


版本平台


平台:高通MSM8974
安卓版本:4.4
Linux内核版本:3.4.4
文件路径: android\kernel\drivers\staging\android\Lowmemorykiller.c

概念:


前文已经介绍过,LMK是定时扫描系统中的内存,当系统内存少于某个阀门值的时候,就会选择性的去释放一些内存(杀死进程),这就是你看到当前的程序突然退出的原因,可能有人会问,它系统怎么不做的人性化点,检测到我在使用,不要杀我使用的程序啊,去杀别的啊,这个我们后面慢慢道来~~


核心结构及调用


阀门值

LMK杀死内存就是根据阀门值来选择去杀掉哪些进程,我们先看看与阀门值有关的两个数据结构:
static int lowmem_adj[6] = {
	0,
	1,
	6,
	12,
};
static int lowmem_adj_size = 4;
static int lowmem_minfree[6] = {
	3 * 512,	/* 6MB */
	2 * 1024,	/* 8MB */
	4 * 1024,	/* 16MB */
	16 * 1024,	/* 64MB */
};
static int lowmem_minfree_size = 4;
这两个数据结构是仅仅相互关联的,那么他们是怎么相互协作的工作的呢?举个列子:
当前系统空闲内存是63M时,比lowmem_minfree[3]小且大于lowmem_minfree[2],那么当前要杀掉进程的标准的值(每个进程都有一个adj值)就是lowmem_adj[3],也就是说找到所有进程的adj值大于lowmem_adj[3]的进程,然后从他们之中找到adj值最大的杀掉,如果有两个进程的adj一样时,会选择内存占用最多杀掉!数组其他部分以此类推
    Android将进程分为6个等级,foreground(前台进程)、visible(可见进程)、secondary server(次要服务)、hidden(后台进程)、content provider(内容供应节点)、empty(空进程),优先级依次降低,所以可以看到最容易被杀死的就是空进程,最不容易被杀死的就是前台进程,所以当你正在使用着的应用就被无辜杀死的时候,说明此时系统的阀门值已经触底,你是不是开了很多应用?

lowmem_shrink():

LMK示意内核模块的形式注册进内核,在初始化的时候,会初始化注册一个shrink()(关于Linux的shrink的知识这里不赘述),当空闲内存触及到阀门值的时候,就会调用到lowmem_shrink()这个函数,这个函数也就是选择并且杀死进程最核心的操作地方,我们细细来看它的过程和代码:
	   p = find_lock_task_mm(tsk);   /*有mm的thread才能操作*/
		if (!p)
		  continue;
		/*得到task对应的oom_score_adj*/
		oom_score_adj = p->signal->oom_score_adj;
		/*比当前的基本标准小就忽略。*/
		if (oom_score_adj < min_score_adj) {
			task_unlock(p);
			continue;
		}
首先会扫描进程列表,查看当前进程是否有mm,只有有mm的进程才能执行以下操作,如果找到了就获得当前进程的oom_score_adj的值(即进程的adj的值),然后判断当前进程的adj值是不是比基准值小( 要找到比基准值大的,且是最大的进程杀死,不要忘记前面刚说过哦~)
		/*获得此进程的rss内存占用大小*/
		tasksize = get_mm_rss(p->mm);
		task_unlock(p);
		if (tasksize <= 0)
			continue;
如果找到一个比基准值大的,那么记录它的内存大小,以备后面遇到两个adj值一样大小的进程可以比较谁内存占据的多就杀死谁。
		if (selected) {
			/*如果当前task的oom score比上次小,则不做处理*/
			if (oom_score_adj < selected_oom_score_adj)
				continue;
			/*如果前后两个task的oom score一样,而且此task 占有
<span style="white-space:pre">			</span>内存比上次的task小时,也不做处理。*/
			if (oom_score_adj == selected_oom_score_adj &&
			    tasksize <= selected_tasksize)
				continue;
		}
下面的问题就是最简单的问题,两两比较找到一个链表中adj最大的值,如果adj相同,找到占用内存tasksize最大的值,这样就会选择出要杀死的进程是谁了,接着往下看:
		selected = p;
		selected_tasksize = tasksize;
		selected_oom_score_adj = oom_score_adj;
		lowmem_print(2, "select %d (%s), adj %d, size %d, to kill\n",
			     p->pid, p->comm, oom_score_adj, tasksize);
	}
这段代码就是更新selected的值,以便下一次比较,时刻保持selected中的adj值是最大的,并且它的占用内存是最大的,那当然因为下面我要杀死它释放内存了~~
开始杀了:
	if (selected) {
		lowmem_print(1, "send sigkill to %d (%s), adj %d, size %d\n",
			     selected->pid, selected->comm,
			     selected_oom_score_adj, selected_tasksize);
		lowmem_deathpending_timeout = jiffies + HZ;
		send_sig(SIGKILL, selected, 0); 		/*发送SIGKILL信号杀死进程*/
		set_tsk_thread_flag(selected, TIF_MEMDIE);
		rem -= selected_tasksize;
	}
	lowmem_print(4, "lowmem_shrink %lu, %x, return %d\n",
		     sc->nr_to_scan, sc->gfp_mask, rem);
	rcu_read_unlock();
	return rem;
}
看到了没,最终就是调用了SIGKILL这个信号让Linux内核去杀死进程的,借刀杀人,此法还是极好的。

修改lowmem_adj和lowmem_minfree的值

Android 4.0 以后,改变了4.0以前操作这两个数组的地方,操作这两个数据大小的文件路径:
android\frameworks\base\services\java\com\android\server\am\ProcessList.java
private final int[] mOomAdj = new int[] {
    FOREGROUND_APP_ADJ, VISIBLE_APP_ADJ, PERCEPTIBLE_APP_ADJ,
    BACKUP_APP_ADJ, HIDDEN_APP_MIN_ADJ, HIDDEN_APP_MAX_ADJ
};
   // These are the low-end OOM level limits.  This is appropriate for an
    // HVGA or smaller phone with less than 512MB.  Values are in KB.
    private final long[] mOomMinFreeLow = new long[] {
            8192, 12288, 16384,
            24576, 28672, 32768
    };
    // These are the high-end OOM level limits.  This is appropriate for a
    // 1280x800 or larger screen with around 1GB RAM.  Values are in KB.
    private final long[] mOomMinFreeHigh = new long[] {
            49152, 61440, 73728,
            86016, 98304, 122880
    };
    // The actual OOM killer memory levels we are using.
    private final long[] mOomMinFree = new long[mOomAdj.length];
最终通过某个运算,将oOmMinFreeLow和mOomMinFreeHigh经过运算,最后得出的值存入mOomMinFree中,而如何计算这个值,是根据当前屏幕的分辨率和内存大小来,从英文注释中可以看出。
最后这个值的更新在函数updateOomLevels中,该函数也在ProcessList.java中
        for (int i=0; i<mOomAdj.length; i++) {
            long low = mOomMinFreeLow[i];
            long high = mOomMinFreeHigh[i];
            mOomMinFree[i] = (long)(low + ((high-low)*scale));
        }
scale的值根据内存和分辨率的情况来确定,它只有两个值要不是0,要不是1,后面还有一些容错运算,然后最后来确定mOomMinFree的值,是不是感觉很繁琐?是的,我也感觉很繁琐!
       for (int i=0; i<mOomAdj.length; i++) {
            if (i > 0) {
                adjString.append(',');
                memString.append(',');
            }
            adjString.append(mOomAdj[i]);
            memString.append((mOomMinFree[i]*1024)/PAGE_SIZE);
        }
。。。。。 //此处省略一些代码
//Slog.i("XXXXXXX", "******************************* MINFREE: " + memString);
        if (write) {
            writeFile("/sys/module/lowmemorykiller/parameters/adj", adjString.toString());
            writeFile("/sys/module/lowmemorykiller/parameters/minfree", memString.toString());
            SystemProperties.set("sys.sysctl.extra_free_kbytes", Integer.toString(reserve));
        }
        // GB: 2048,3072,4096,6144,7168,8192
        // HC: 8192,10240,12288,14336,16384,20480
最终,等待更新快结束的时候,它把值写入到系统中,所以可以在节点/sys/module/lowmemorykiller/parameters/adj 和 minfree中查询到当前的阀门值。

总结


说了那么多代码,也许你看的有点枯燥了,下面我们说点实际的,也是我的几点感受总结。

1.系统进程不会被LowmemoryKiller被杀掉,因为重要级别的系统应用的adj的值都被设置为-16,-12这种级别,上层有那么多大于0的进程,它们是很安全的。

2.使用过程中,发现几点,如果内存偏小,比如1GB的内存,那么经常会有进程会被杀死,这就是为什么聊qq的时候,突然来了一条短信,你查看玩短信在回到qq,有时候需要重新载入,有时候直接可以回到退出前的界面,取决于qq有没有被杀死

3.小内存的机子耗电:这个是真的,有些手机优化的不好,会发生这样的情况,有的进程不断被杀死,然后又自己起来了(这种进程很多,有的是注册了服务,只要检测到自己的主体被杀掉,它会适当的时候再启动一个),然后又被杀掉,然后又起来,周而复始,这样手机耗电量就上来了。你也许遇到过也许没有遇到,那就是你的桌面一会正常着,一会又显示正在载入,这就是桌面的重启服务在和lowmemorykiller周旋。

4.为什么安卓那么耗费内存?安卓用的是Java虚拟机,Java程序员的一个很不好的习惯就是内存只开辟,不手动释放,等着系统垃圾回收来释放。安卓用的Linux内核,Linux内核与Windows不同,它的思想就是把物理内存用的越多越好,这样利用率高,与Windows没事别开那么多程序的想法不同的,它有自信可以为用户提供更好的体验。安卓让一些程序开机就启动,是为了让用户点开的之前已经预加载过,点击的时候载入更快,想法是好的,但是对于厂商那些内置的垃圾应用,我又不用它,让它终年占着我的内存是什么情况?谁给解释解释?

5.推荐一个设置阀门值的文章:如何合理设置阀门值

6.买个大内存的手机,至少2GB以上,现在也不贵,2000元左右的,也不贵,买个好点的手机,用起来更加舒畅,心情也好,做事也舒心,不要在1GB的手机上太纠结,活着还是开心好。

7.谷歌说的512M的内存上运行流畅,我先呵呵了,坐等Android One的表现
Android oom_adj 内存水位算法剖析 从上文Android lmkd 机制详解中了解到lmkd 兼容了新的PSI 策略,以及旧是的kernel 驱动程序和oom adj 策略。Android 进程在不同的时候处于不同的进程状态,也会根据重要性动态调整进程的oom score。这样在lmkd 中可以根据当前的内存使用情况,找到合适的oom_score_adj,并将其kill 以满足内存的持续使用。本文主要剖析oom adj 算法,可以接Android lmkd 机制详解中第 8 节。 阅读详情

相关推荐

你真的了解 Android LMK 机制么?

上文主要介绍了Andorid内存的管理机制,本文对其中的LMK机制进行深入扩展总结。 我们知道出现Crash应用闪退和崩溃一般有三个原因:ANR(程序无响应)、Exception(异常)、LMK(低内存杀死机制)。本文重点介绍LMK机制。 目的:了解LMK原理,探究进程保活的方案和程序异常处理的方法。

小羊子的技术专栏 9373

Android LowMemoryKiller 初识

lowmemorykiller: Killing 'apps.searchlite' (27533:27533), adj 900, 一、内容预览 1、为什么引入LowmemoryKiller?   进程的启动分冷启动和热启动,当用户退出某一个进程的时候,并不会真正的将进程退出,而是将这个进程放到后台,以便下次启动的时候可以马上启动起来,这个过程名为热启动,这也是Android的设计...

liu362732346的博客 719

Android系统 实现低内存白名单防LMK原理分析

本文介绍低内存LMK的原理以及实现过滤某个应用不会被LMK杀死,即使它的OOM_ADJ值很高,也不会受到OOM_MINFREE值的限制。这样可以保证该应用的后台服务和功能正常运行,比如接收消息、执行任务等。

我其实什么都不会 2089

Android 学习

Android Low Memory Killer Daemon (lmkd) 是一个进程,它监视正在运行的 Android 系统的内存状态,并通过杀死最不重要的进程来对高内存压力做出反应,以使系统性能保持在可接受的水平。从历史上看,在 Android 系统上,内存监控和非必要进程的终止是由内核 lowmemorykiller 驱动程序处理的。自 Linux Kernel 4.12 起,lowmemorykiller 驱动程序已被删除,取而代之的是用户空间 lmkd 守护进程执行这些任务。

qq_41305245的博客 2326

Android lmkd 机制详解(一)

本文将深入剖析Android内存管理机制中的lmkd(Low Memory Killer Daemon)工作原理。lmkd是Android系统中负责监控内存状态并在内存压力过高时终止不重要进程的后台服务。文章详细介绍了从Android 10开始采用的PSI(Pressure Stall Information)监控机制,对比了传统vmpressure方式的差异,并解析了lmkd的初始化流程、核心处理逻辑以及与AMS(Activity Manager Service)的通信机制。同时,文章还深入分析了lmkd

私房菜 1万+

【UE5 C++开发指南】从零配置Visual Studio 2022开发环境

本文详细指导如何从零开始为UE5 C++开发配置Visual Studio 2022环境。文章强调了VS2022与UE5的深度兼容性,并逐步讲解了社区版下载、核心工作负载(如“使用C++的游戏开发”)与组件安装、界面优化、专属工具设置以及首个C++项目的创建与验证流程,帮助开发者高效搭建专业开发环境。

weixin_29016865的博客 395

Android 6.0的lowmemorykiller机制

最近在处理一些lowmemorykiller相关的问题,于是对lowmemorykiller机制作了一个简单的了解。在这里总结一下。 首先,是lowmemorykiller的一些背景知识。 众所周知,Andorid的实质是一个linux的操作系统。所以和其他操作系统一样,每个程序,每个进程运行,都需要一定内存空间进行支撑。而进程的内存空间只是虚拟内存,程序运行需要的是实实在在的

u012440406的专栏 1万+

阿里云一键登录Java后端实战:5分钟搞定号码认证服务集成(附完整代码)

本文详细介绍了如何在Java后端项目中快速集成阿里云号码认证服务(PNVS),实现高效、安全的一键登录功能。文章从核心架构、安全机制解析入手,提供了完整的Maven依赖配置、服务端环境准备、核心业务逻辑实现(包括Token验证、用户处理、异常降级)以及生产环境下的性能优化、监控与部署方案,并附有可直接使用的完整代码示例。

p1q2r的博客 848

lowmemerykiller参数表

OomMinFreeLow: // These are the low-end OOM level limits. This is appropriate for an // HVGA or smaller phone with less than 512MB. Values are in KB. private final int[] mOomMinFreeLow = new int[] { 12288, 18432, 24576, 36864, 43008, 49

859

WSL2迁移后用户名不见了?一招教你快速恢复Ubuntu22.04默认用户

本文针对WSL2迁移后Ubuntu 22.04默认用户名丢失的常见问题,提供了详细的诊断与修复方案。通过分析迁移底层逻辑,指导用户使用`ubuntu2204.exe config --default-user`命令或配置`/etc/wsl.conf`文件快速恢复用户身份,并涵盖权限修复、冲突解决及自动化迁移脚本等进阶内容,确保子系统迁移后环境完整可用。

web39explorer的博客 558

物理专业书库模板(1):物理学书单(适合物理专业本科生、研究生、科研工作者、物理爱好者...)

物理专业书库模板(1):物理学书单(适合物理专业本科生、研究生、科研工作者、物理爱好者...) 求赞求喜欢求分享,建议关注,防止文章丢失。

hepyang的博客 2181

Linux入门基础

Linux入门基础命令 RPM 红帽软件管理 rpm-ivh filenme.rpm 安装软件 rpm-Uvh filenme.rpm 升级软件 rpm-e filenme.rpm 卸载软件 rpm-qpi filenme.rpm 查询软件描述信息 rpm-qpl filenme.rpm 列出软件文件信息 rpm-qf filenme.rpm 查询软件属于哪个RPM Yum 软件仓库 将大量的RPM打包到一起,进一步降低安

weixin_47115091的博客 305

详细推导波特图增益和相位曲线

文章来源:https://mp.weixin.qq.com/s/UCy9mRRok5Hc9qHq3qde5g 今天这篇文章想把近段时间关于波特图方面学习的内容整理一下。波特图将会是梳理闭环控制这个系列的开始。目前的计划是,以无源器件为基础,然后结合运放,再过渡到开关电源的环路控制方面。如果对这方面感兴趣的朋友,可以持续一起学习起来,目标就是掌握开关电源的闭环控制。 1. 什么是波特图? 波特图最常见的应用是在线性非时变的系统中,用于对传递函数的增益和相位进行分析,从而判断出系统随频率的响应是否稳定。因此,波

ltqdxl的博客 2万+

关于OOM和内存回收的一点浅显的认识

Low Memory Killer在用户空间中指定了一组内存临界值,当其中的某个值与进程描述中的oom_adj值在同一范围时,该进程将被Kill掉。 Android的每一个应用都是运行在一个独立的DVM中,他们之间互不影响;应用退出之后,并没有立马杀死进程,进程依然停留在内存中,这么做的目的是为了提高下次启动时的速度。而在Android中管理进程的模块是AMS,主要有LRU weight、OOM adj、Low MemoryKiller共同来完成进程的管理。

大尉的博客 3665

浅谈向量检索

文章目录浅谈向量检索背景什么是向量什么是向量检索距离度量检索方法ANN的基本思路举个容易理解栗子举个正常的例子具体算法树方法KD-TreeAnnoyHash方法LSH 算法矢量量化方法乘积量化码本的建立码字搜索算法倒排乘积量化临近图方法HNSW 算法朴素想法Delaunay算法NSW 主要思想NSW构图NSW查找参考 浅谈向量检索 背景 索引一直被认为是检索引擎最重要的组成部分,之所以数据库能够快速的查询出来结果。系统设计中很多常见的多种数据结构都是为了快速命中索引设计的, 比如 红黑树、跳跃表、倒排索引、

qq_42828391的博客 6553

AD7768与STM32F439的硬件设计避坑指南:如何优化高精度ADC系统的电源与PCB布局

本文详细解析了AD7768与STM32F439在高精度ADC系统硬件设计中的关键问题与优化方案。从电源系统设计、PCB布局到基准电压选型,提供了实用的避坑指南和实测数据,帮助工程师提升系统性能至110dB动态范围。特别强调了SPI信号布线和电源滤波对ADC精度的重要影响。

sugar的博客 739

Android系统进程优先级策略 ADJ

本篇文章是基于Android9.0来介绍Android系统关于进程的优先级是如何定义和管理的。 概述 1.进程 进程-Process 是程序的一个运行实例。通常会有唯一一个pid与之对应。但pid不是绝对唯一的,当进程死亡后pid会被回收给另外的进程使用。在Android世界里,App开发者很容易认为系统的四大组件就是进程的载体,实际上,它们不能算是完整的进程实例,最多只能算是进程的组成部分。由于Android系统框架中,系统对进程的创建和管理进行了封装,每当我们在启动四大组件Activity, Br

w2064004678的博客 6153

【数据结构】考点二十八 : 二叉树的概念:二叉树的度、完全二叉树、满二叉数结点数、哈夫曼结点数、完全二叉树的深度

二叉树(Binary Tree)是一种常见的数据结构,它或者为空(null),或者由一个根节点(root node)和两个子树(left subtree 和 right subtree)组成,这两个子树又分别是一棵二叉树。二叉树的每个节点最多有两个子节点,通常称为左子节点(left child)和右子节点(right child)。

超越的博客 1738

Android的LMK和OOM

Android分析之LowMemoryKiller linux操作系统的传统理念就是内存用的越多越好,尽可能拿来用,既然被尽量的使用,自然应该有清除机制。Android以linux为基础,自然部分继承了这个特性。Android使用lowmemorykiller在达到某个内存门限的情况下去选择进程删除来释放内存。关键的配置文件有如下两个,/sys/module/lowmemorykiller/pa

wuhengde的专栏 4739

android 蓝牙通讯demo

android 下蓝牙通讯demo,实现了发现蓝牙,使能蓝牙,通过蓝牙发送消息。

汉书HE8,好用的汇编代码编写工具

汉书HE8,好用的汇编代码编写工具早期编写汇编代码所用功能强大

Low Memory Killer in Android

Android LMK机制以及如何使用LMK机制为我们服务

我的博客 2616

lwip-2.0.3

lwip是瑞典计算机科学院(SICS)的Adam Dunkels 开发的一个小型开源的TCP/IP协议栈。实现的重点是在保持TCP协议主要功能的基础上减少对RAM 的占用。

android系统(100)---如何减少lowmemory的发生几率

如何减少lowmemory的发生几率1. 内存问题  内存不足时常发生卡顿,甚至是死机重启问题,需要尽量减少内存不足情况的发生 2.解决方法   (1)减少后台进程的数量修改frameworks\base\services\core\java\com\android\server\am\ProcessList.javastatic final int MAX_CACHED_APPS = 32; /...

zhangbijun1230的专栏 1822

C# WinForm 基于DirectShow.dll编写的程序,可边预览边保存视频,音频和图片

基于微软的DirectShow.dll的二次开发1.自动识别连接的摄像头跟麦克风且可以对其进行选择(默认选中第一个)2.可录制wmv和avi两种格式的视频并且都带有音频,avi可以录制的分辨率和帧数进行选择,wmv默认进行了淸晰度优化如不喜欢可去除代码中有相关提示3.可在预览点开之后进行拍照,也可边录制视频边拍照4.添加了视频和音频的压缩功能(酌情使用)这次二次开发发现C#对DirectShow的相关信息太少,这次做完也不敢藏私共享出来,还望大家指正

Android的LMK机制学习笔记

详细介绍了Android的Low Memory Killer (LMK)机制。分享了自己在处理后台应用程序被杀死问题时的分析过程。LMK机制是基于Linux的OOM killer修改而来,主要通过驱动层实现,定期检查系统可用内存,并根据预设的内存阈值,杀死优先级低的进程。文章还深入解析了lowmemorykiller.c文件的核心逻辑,包括内存阈值的设定、进程遍历和查杀流程。文中还探讨了内核层面的log信息,以及AMS如何通知被杀进程进行数据清理。

TechMix的博客 2180

[Android] [AOSP] LMK进程优先级策略

在AOSP中,Low Memory Killer(LMK)是一种内存管理机制,它根据进程的优先级来决定哪些进程应该被终止以释放内存。这个机制通过内核任务(kswapd)来实现,该任务不断扫描内存使用情况,并在内存不足时触发LMK机制。在LMK机制中,每个进程都被分配了一个优先级,当内存不足时,系统会根据进程的优先级选择性地终止某些进程,以释放更多的内存。

q1210249579的博客 1183

进程保活之LMK(LowMemoryKiller)的理解

想要了解有关进程优先级请自行查看上一篇文章:https://blog.csdn.net/qq_36451275/article/details/95454643 这篇文章我们来了解一下有关LMK(LowMemoryKiller),咋一看这个大家应该都不怎么了解,对于这一块我也不是很了解,最近看到相关的资料就简单的进行了整理。 一、为什么要引入LMK呢? 进程的启动分冷启动和热启动,当用户退出...

qq_36451275的博客 1296
上一篇: 巧妇能为少米之炊(1)——Android下小内存下的生存之道
下一篇: 巧妇能为少米之炊(3)——压缩饼干(ZRAM)
memoryjs
博客等级 码龄18年 240粉丝 113原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值