Android内存笔记之基础知识篇
文章目录
内存是性能的一个重要指标,能直接影响用户的体验,特别是OOM会直接导致进程crash。
而内存是一个庞大的知识体系,一两句话没办法讲清楚。所以会以一个系列文章来巩固内存知识。
一、何为内存?
由计算机原理的知识我们可知,内存是一种读写速度快,关机即丢失数据的硬件。CPU对数据的读写都需要通过内存。
二、Java内存简述
Android上的App都是运行在Android虚拟机上的(5.0以下使用Dalvik虚拟机,5.0以上使用ART虚拟机),而Android虚拟机本质上就是Java虚拟机,所以Android的内存和Java的内存是密切相关的。要探究Android内存,要先明白Java内存模型。
1、JVM内存模型
这题可谓是面试高频题,网上也是有无数文章来讲解。最经典的就是下面这张图。
- 方法区: 这里保存了类的信息,静态变量,常量等,是线程共享的。
- 虚拟机栈:保存了局部变量表、操作栈、动态链接、方法出口、对象指针。这里面的变量是引用。线程私有。
- 本地方法栈: 这部分是为Native方法服务的,也就是c或c++函数。线程私有。
- 堆: **每一个对象的分配都是在堆上进行的,虚拟机栈里分配的是引用,这些引用会指向堆里真正的对象。**线程共享
- 程序计数器:记录当前线程执行到了第几行。线程私有。
通过一段简单代码说明一下:
// 类名、常量池等信息存储在方法区
public class Company {
// 存储在方法区
public static int DEFAULT_AGE = 10;
// 成员变量在堆里
private Map<String, Float> mSalary = new HashMap();
private void createSalary() {
// 临时变量在虚拟机栈里
Map<String, Float> salary = new HashMap<>();
}
}
2、JVM的垃圾回收GC
得益于JVM的垃圾回收策略,Java代码并不需要像C/C++代码那样管理内存的分配与回收。
在C/C++里,需要通过malloc等方法来申请内存,并在使用完毕后通过free等方法释放。而在Java里,我们new出来一个对象时候,大多时候并不关心何时去释放。这都是JVM帮我们做了回收内存的工作,称为垃圾回收GC(Garbage Collection)。
2.1、如何判断哪些类该被回收?
判断一个类是否该被回收,JVM采用的是可达性策略。
虚拟机会定义一系列的GC Root,如果一个对象是从GC Root出发可达的,那就不进行回收。如果这个对象从任何GC Root出发都不可达,虚拟机就会认为这是一个不再使用的对象,进行回收。
以下是GC Root
- 虚拟机栈(栈帧中的本地变量表)中引用的对象
- 本地方法栈中 JNI(即一般说的 Native 方法)引用的对象
- 方法区中类静态属性引用的对象
- 方法区中常量引用的对象
如上图中,a和b都是可被回收的对象。
2.2、GC算法
- 标记-清除法
- 算法: 标记出所有需要回收的对象(GC Root不可达的),然后一次性回收掉这些对象。
- 优点: 简单
- 缺点: 容易造成很多不连续的内存碎片,导致内存无法充分利用。效率较低。
- 复制算法
- 算法: 将内存分为两块,每次只使用其中一块。在gc时将还存活的对象复制到另一块上,然后把已使用的部分清理掉
- 优点: 运行简单。
- 缺点: 资源浪费,可用内存为最大内存的一半。
- 标记-整理算法
- 算法: 标记出所有需要回收的对象(GC Root不可达的),然后将存活的对象移动到内存的一端,最后清理掉需要回收的对象。
- 优点: 避免产生连续的碎片内存,能充分利用内存
- 缺点: 开销相对较大
- 分代收集算法
- 算法: 将对象分为新生代和老年代,每一代都有自己的回收策略。新生代使用复制算法,老年代使用标记-整理算法。
- 优点: 结合了各类算法的优点。
目前虚拟机采用的是分代回收算法,年轻代因为存活的对象较少,每次GC都会回收大量对象,采用复制算法只需要付出复制少量存货对象的开销。而老年代的存活对象较多,就采用标记-整理算法。
三、Android内存
第二节提到,Android的App是运行在Android虚拟机上的,但是不代表Android的内存就只有虚拟机这里的内存,实际上Android的内存组成比想象的复杂。
Android是一个操作系统,本身是基于Linux的开发的,所以内存管理方面也是基于Linux的。由于篇幅原因本文就不说明linux的内存管理了。
dumpsys meminfo
这个命令是用来查看内存使用情况的。直接使用这个命令会输出按RSS和PSS排序的内存信息,按以下分类:
类型 | 解释 |
---|---|
process | 按进程排序 |
OOM adj | 按Native、System、Persistent等划分显示每一类的进程情况 |
category | 按Dalvik、Native、.art mmap等划分显示每一类的进程情况 |
但是直接使用这个命令的情况比较少,通常是加上进程号来查看进程占用的内存情况。
adb shell dumpsys meminfo <pid>
通过这个命令得到的内存信息,就是Android内存的组成部分了。
1、什么是Pss Rss Vss
从图里我们可以看到Pss Total和Rss Total,先解释一下是什么意思。
- Pss Proportional Set Size 使用的实际物理内存大小
- Rss Resident Set Size 使用的实际物理内存大小
- Vss Vitual Set Size 使用的虚拟内存大小
Pss和Rss都是指占用的物理内存,但计算方法有一点不同。
- Rss在计算时会算上使用的共享库总大小。
- Pss在计算时会将共享库的大小按照使用的进程等比例分配。
假设A进程使用了500KB的物理内存,他和B进程都使用了一个100KB的共享库,那么A进程的RSS = 500 + 100 = 600KB,而Pss = 500 + 100/2 = 550KB。
通常Pss比较有参考价值。
Vss是虚拟内存占用的大小。因为c/c++通过malloc()函数申请的是虚拟内存,如果这块内存没有被访问,是不会有实际物理内存占用的。只有在访问时,发生了缺页才会去真正的分配物理内存。
2、各部分含义
第一部分
先来看看横轴的含义。
名称 | 含义 |
---|---|
Pss Totoal | 所占用的总的Pss |
Private Dirty | 进程私有、与磁盘相比有改变的内存数据 |
Private Clean | 进程私有、与磁盘相比没有改变的内存数据 |
SwapPss Dirty | linux有个ZRAM特性的特性,开启的时候显示 |
Rss Total | 所占用的总的Rss |
再来看看纵轴
名称 | 含义 |
---|---|
Native Heap | C/C++代码所分配的内存 |
Dalvik Heap | Android 虚拟机分配的内存 |
Dalvik Other | 类数据结构、索引分配的内存 |
Stack | 占内存 |
Cursor | CursorWindow 占用的空间,与 SQL 有关 |
Ashmem | 匿名共享内存,此类内存与 cache shrinker 关联,可以控制cache shrinker在适当时机回收这些共享内存 |
Gfx dev | /dev/kgsl-3d0占用的内存 |
Other de v | 内部driver占用的内存 |
.so mmap | 映射的.so代码占用的内存 |
.jar mmap | Java文件代码占用的内存 |
.apk mmap | apk代码占用的内存 |
.ttf mmao | ttf文件代码占用的内存 |
.dex mmap | 映射的.dex代码占用的内存 |
.oat mmap | 代码影映像占用的内存。 |
.art mmap | 堆映像占用的代码 |
Other mmap | 其他文件占用的内存 |
第二部分
名称 | 组成 |
---|---|
Java Heap | Dalvik Heap的Private Dirty .art mmap的Private Dirty + private Clean |
Native Heap | Native Heap的Private Dirty |
Code | .so mmap .jar mmap .apk mmap .ttf mmap .dex mmap .oat mmap的Private Dirty + Private Clean |
Stack | Stack的private Dirty |
Graphics | Gfx dex EGL mtrack GL mtrack的P rivate Dirty + Private Clean |
Private Other | Native Heap Dalvik Heap Heap_UNKNOWN的Private Dirty + Private Clean |
System | Native Heap Dalvik Heap HEAP_UNKNOWN的Pss + SwapPss Dirty - Private Dirty - Private Clean |
TOTAL PSS | Native Heap Dalvik Heap HEAP_UNKNOWN的Pss + SwapPss Dirty |
TOTAL SWAP PSS | Native Heap Dalvik Heap HEAP_UNKNOWN的SwapPss Dirty |
参考文章:
https://www.jianshu.com/p/9edfe9d5eb34
https://juejin.cn/post/6844904096541966350
https://developer.android.google.cn/studio/command-line/dumpsys?hl=zh-cn