Android内存笔记之基础知识篇

Android内存笔记之基础知识篇


内存是性能的一个重要指标,能直接影响用户的体验,特别是OOM会直接导致进程crash。

而内存是一个庞大的知识体系,一两句话没办法讲清楚。所以会以一个系列文章来巩固内存知识。

一、何为内存?

由计算机原理的知识我们可知,内存是一种读写速度快,关机即丢失数据的硬件。CPU对数据的读写都需要通过内存。

二、Java内存简述

Android上的App都是运行在Android虚拟机上的(5.0以下使用Dalvik虚拟机,5.0以上使用ART虚拟机),而Android虚拟机本质上就是Java虚拟机,所以Android的内存和Java的内存是密切相关的。要探究Android内存,要先明白Java内存模型。

1、JVM内存模型

这题可谓是面试高频题,网上也是有无数文章来讲解。最经典的就是下面这张图。

在这里插入图片描述

  1. 方法区: 这里保存了类的信息,静态变量,常量等,是线程共享的。
  2. 虚拟机栈:保存了局部变量表、操作栈、动态链接、方法出口、对象指针。这里面的变量是引用。线程私有。
  3. 本地方法栈: 这部分是为Native方法服务的,也就是c或c++函数。线程私有。
  4. 堆: **每一个对象的分配都是在堆上进行的,虚拟机栈里分配的是引用,这些引用会指向堆里真正的对象。**线程共享
  5. 程序计数器:记录当前线程执行到了第几行。线程私有。

通过一段简单代码说明一下:

// 类名、常量池等信息存储在方法区
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算法

  1. 标记-清除法
    • 算法: 标记出所有需要回收的对象(GC Root不可达的),然后一次性回收掉这些对象。
    • 优点: 简单
    • 缺点: 容易造成很多不连续的内存碎片,导致内存无法充分利用。效率较低。
  2. 复制算法
    • 算法: 将内存分为两块,每次只使用其中一块。在gc时将还存活的对象复制到另一块上,然后把已使用的部分清理掉
    • 优点: 运行简单。
    • 缺点: 资源浪费,可用内存为最大内存的一半。
  3. 标记-整理算法
    • 算法: 标记出所有需要回收的对象(GC Root不可达的),然后将存活的对象移动到内存的一端,最后清理掉需要回收的对象。
    • 优点: 避免产生连续的碎片内存,能充分利用内存
    • 缺点: 开销相对较大
  4. 分代收集算法
    • 算法: 将对象分为新生代和老年代,每一代都有自己的回收策略。新生代使用复制算法,老年代使用标记-整理算法。
    • 优点: 结合了各类算法的优点。

目前虚拟机采用的是分代回收算法,年轻代因为存活的对象较少,每次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

猜你喜欢

转载自blog.csdn.net/qq_43478882/article/details/129227950