Android的内存分配、管理、OOM这一篇文章就够够的了!

Android的内存分配和管理为两个大板块: (Android手机系统和手机应用APP)

一、Android 手机系统的内存分配和管理

主要3点内容介绍
-Android 系统的低内存工作机制:
-Android 应用内存的使用情况:
-如何减少Android 应用内存的占用:

1、Android 系统的低内存工作机制:

首先给大家提出一个问题,为什么有的手机打开应用多了,在使用的时候回特别卡呢。并且有时候回卡在APP里面好长时间呢?甚至黑屏,重启这些现象呢?那么今天我就给大家一一揭晓谜底:

先给大家介绍一个物理内存概念: 然后引入Android Low Memory Killer。(低内存杀手)

手机内存(物理内存): 设备的物理内存被分为很多页(Page),每页4KB, 不同的页用来做不同的事情:
-橘黄色代表已使用的页
-黄色代表缓存页(数据在磁盘上有备份,所以Cache Pages是可以被回收的)
-绿色是空闲页

在这里插入图片描述

用于回收Cached Pages(缓存页内存)的kswapd进程
Cached Pages缓存页什么时候被回收呢? 下面我们分析一下

下面是一个2GB内存的手机,X轴代表使用时间, Y轴代表内存情况。随着打开的应用越来越多,Userd Pages(使用内存)也越来越多,而Cached Pages(缓存页)和Free Pages(空闲页)越来越少。当Free Pages(空闲页)低于kswapd(回收进程)的阀值时,linux内核就会通过kswapd进程Cached Pages(缓存页)就行回收。当应用再次访问Cached Pages(缓存页)的内容时,就需要从磁盘上重新加载。如果Cached Pages(缓存页)太少,设备可能就死机。
在这里插入图片描述
所以Androd 上有个机制叫做 Low Memory Killer,当 Cached Pages 太少时,就会被触发。它的工作方式是根据进程的优先级,选择性地杀死某个进程,释放该进程占用的所有资源以满足内存分配需要:
在这里插入图片描述
如上图所示,当 Cached Pages 低于 LMK 阈值时,将会触发低内存杀死机制。

LMK(Low Memory Killer)
如果 LMK 杀掉的是用户正在交互或可以感知的进程,将会导致非常不友好的用户体验。所以 Android SystemServer 进程维护了一张进程优先级列表,LMK 根据这张表来决定先杀死哪个进程:
在这里插入图片描述
-Perceptible 指的是非用户直接交互的进程,比如在后台播放音乐的音乐播放器进程;
-Previous 指的是切换至当前前台应用前的应用进程;
-Cached 指缓存的进程,这可能是退至后台的应用进程,也可能是已经退出的应用进程,目的是为了实现应用间的快速切换。所以,Cached 进程也是优先级最低的进程:

在这里插入图片描述
如上图所示,当已用内存超过 LMK 阈值时,LMK 将从 Cached 列表底部开始杀死进程。如果可用内存还是不满足分配需要,那么将会按照上表所示优先级自底向上杀死进程,直到准备 Kill SystemServer 进程,这将导致手机重启。

所以,你可以想象 LMK 在低内存手机上的情景:如图所示
在这里插入图片描述

以上就很好的解释了我文章开始提问那个问题,有的手机为什么会卡顿等等。原因就是当手机内存小,同时使用的使用时间过长,已使用的内存越来越多,缓存页和空闲页越来越少,LMK 将一直处于活跃状态,具体表现就是应用卡顿、桌面黑屏重启,手机死机等等。

所以在开发中,有的同事拿来特别low的手机,给你说,你看我们APP特别卡,怎么回事?要求你解决这个问题等等, 你就可以大声怼他了!哈哈哈…

2、Android 应用内存的使用情况:

那么,我们怎么知道 App 使用了多少内存呢?

物理内存追踪

之前提到,设备的物理内存被分为很多页(Page),Linux Kernel 将会持续跟踪每个进程使用的 Pages,所以只要对进程使用的 Pages 进行计数即可:
在这里插入图片描述
但实际情况远比这要复杂的多,因为有些 Pages 是进程间共享的:
共享内存页计数方法

1、RSS(Resident Set Size):App 完全负责
在这里插入图片描述
2、PSS(Proportional Set Size):App 按比例负责,比如下图所示两个进程共享,那就负责一半。如果三个进程共享,那就负责三分之一:
在这里插入图片描述
3、USS(Unique Set Size):App 无责
在这里插入图片描述
但实际上,至少需要系统级别的上下文才能知道识别 RSS 与 USS。所以通常都是使用 PSS 来计算,这也可以避免多计或者少计 Shared Pages。你可以使用:

adb shell dumpsys meminfo -s [process] 

命令来查看一个进程的 PSS 使用情况:
最底部的 TOTAL 代表的就是应用按比例占用的总内存大小。
在这里插入图片描述

减少应用内存占用

使用 Android Studio 的 Memory Profiler,可以查看当前 Java 堆上分配了哪些对象、对象大小以及对象引用链和被引用链等很多信息。Live Allocation 中有 image heap、zygote heap、app heap 等可以选择,但是我建议你只关注 app heap。因为 image heap 和 zygote heap 是 App 启动时从系统继承过来的,对于这部分内存占用,我们基本上无能为力:

在这里插入图片描述
内存优化建议:
1、优化Java堆上的对象
2、减小apk体积,因为很多在apk中占据磁盘空间的文件,在运行期也会占据内存的空间。


二、手机应用APP的内存分配和管理

-APP的内存是如何分配的?

当我们安装一个应用完成以后,手机系统会为其分配一块内存空间(根据手机内存的大小分配,是有上限的)。当然我们也可以配置最大的分配内存:如下

<application
     .....
          android:largeHeap="true">   这个属性可以理解成扩容用的,就是让手机为自己开辟内存的时候多给一点,具体给多少每款手机不固定,即时是官方设定了值,还多手机厂商还是对这个值进行了修改。
    .......
</application>
-Java代码的内存分配

上篇文章我也介绍了虚拟机的内存分配,对内存有有疑问的可以看上篇虚拟机内存。
OOM,垃圾回收我们针对的是堆内存我们通过代码分析一下, 内存是怎么分配到堆内存里面去的。

public class A{
	int a = 1;
	Test mTest = new Test();

	public void method(){
		int a2 = 1;
		Test mTest2 = new Test();
	}
}

A mA = new A();

我上面写的代码,基本程序中都会这样的,我们分析一下
method方法中的a2和对象引用mTest2,都放在栈中(也就是我上篇讲到的方法中运行是在栈中),但是mTest实例的对象是放在堆中,
mA对象实例存放在堆中,包括这个对象里面所有的成员变量a和mTest也是存在堆中,而mA这个引用也是存在栈中。

经过上面代码的分析,大家在解决内存泄漏的时候会很容易着手了。

得出结论

1.局部变量的基本数据类型和引用存储于栈中,引用的对象实体存储于堆中。因为它们属于方法中的变量,生命周期随方法而结束。
2.成员变量全部存储与堆中(包括基本数据类型,引用和引用的对象实体),因为它们属于类,类对象终究是要被new出来使用的。
3.我们所说的内存泄露,只针对堆内存,他们存放的就是引用指向的对象实体。

什么情况会触发GC回收

先解释一下垃圾回收收集器GC机制,它是自动完成的,它是从根节点开始遍历查询运行的对象,有没有被引用,标志是不是垃圾,如果是垃圾就会回收掉!它针对的只是堆内存。

GC触发时机:
-GC_CONCURRENT: 当我们应用程序的堆内存快要满的时候,系统会自动触发GC操作来释放内存(到达分配内存的阈值)。
-GC_FOR_MALLOC: 当我们的应用程序需要分配更多内存,可是现有内存已经不足的时候,系统会进行GC操作来释放内存。
-GC_HPROF_DUMP_HEAP: 当生成HPROF文件的时候,系统会进行GC操作,关于HPROF文件我们下面会讲到。
-GC_EXPLICIT: 这种情况就是我们刚才提到过的,主动通知系统去进行GC操作,比如调用System.gc()方法来通知系统。或者在DDMS中,通过工具按钮也是可以显式地告诉系统进行GC操作的。

为什么会产OOM?

假如手机为我们应用开辟了100M内存, 而这块内存规定阈值规定的是到达使用70M,触发垃圾回收。 如果应用到达70M,那么会回收垃圾,那么如果代码造成内存泄漏,也就是垃圾对象的实例一直被引用,那么就有可以清理不干净,本应该回收50M,使用可到达20M。但是由于堆空间没有回收干净,只回收了30M,那么我们就使用就会使40M。 经过对比发现会有20M是永远不能回回收的。这时候我们代码越写越多,等到多次GC机制执行以后,造成的垃圾内存大于100M,那么就会出现OOM,从而使应用崩溃。

-如何定位内存泄漏

那么我们如何定位呢,我一般会有用两个办法,
方法1: LeakCanary,可以定位到代码泄漏的地方。

方法2:studio定位:(只解决head里面的内存即可,这个工具也可以定位到代码)
在这里插入图片描述

发布了51 篇原创文章 · 获赞 78 · 访问量 3万+

猜你喜欢

转载自blog.csdn.net/weixin_39079048/article/details/88344491