性能之内存篇

最近笔者在看性能分析相关的是知识,就特意针对内存整理了这一篇文章,在这里笔者主要从下面三个方面来介绍这方面的知识:
1.内存的作用是什么,他在操作系统中的基础知识都有哪一些?
2.查看内存和内存相关问题涉及到的工具都有哪一些,他们的使用方式是什么样子的?
3.碰到内存问题的时候,我们需要怎么去定位呢?

一、内存的基础知识

1.内存的作用:主要用来存储系统和应用程序的指令、数据、缓存等,一般分为物理内存和虚拟内存。

2.物理内存

也称为主存,大多数计算机用的主存都是动态随机访问内存(DRAM),只有内核才可以直接访问物理内存。

3.虚拟内存:

Linux 内核给每个进程都提供了一个独立的虚拟地址空间,并且这个地址空间是连续的,这样,进程就可以很方便地访问这些虚拟内存。

8da8ec96cc7f815ada6536171a60e26c.png

备注:进程在用户态时,只能访问用户空间内存,只有进入内核态后,才可以访问内核空间内存。虽然每个进程的地址空间都包含了内核空间,但这些内核空间,其实关联的都是相同的物理内存,这样以来进程切换到内核态后,就可以很方便地访问内核空间内存。)

虚拟内存的分布介绍:

81999fc565e092305f105c3a44db1fd1.png

1. 栈内存:由系统自动分配和管理。
一旦程序运行超出了这个局部变量的作用域,栈内存就会被系统自动回收,所以不会产生内存泄漏的问题。
2. 堆内存:由应用程序自己来分配和管理。
除非程序退出,这些堆内存并不会被系统自动释放,而是需要应用程序明确调用库函数 free() 来释放它们。
如果应用程序没有正确释放堆内存,就会造成内存泄。
3. 只读段:包括程序的代码和常量,由于是只读的,不会再去分配新的内存,所以也不会产生内存泄漏。
4. 数据段:包括全局变量和静态变量,这些变量在定义时就已经确定了大小,所以也不会产生内存泄漏。
5. 内存映射段:包括动态链接库和共享内存,其中共享内存由程序动态分配和管理。
如果程序在分配后忘了回收,就会导致跟堆内存类似的泄漏问题。

4.内存映射:不是所有的虚拟内存都会分配物理内存,只有那些实际使用的虚拟内存才分配物理内存,并且分配后的物理内存,是通过内存映射来管理的。

dcfe1c1b2f85dd2a674464d95164fd57.png

备注:TLB 是 MMU 中页表的高速缓存,是CPU直接访问的地方。由于进程的虚拟地址空间是独立的,而 TLB 的访问速度又比 MMU 快得多,可以通过减少进程的上下文切换,减少 TLB 的刷新次数,这样来提高 TLB 缓存的使用率,进而提高 CPU 的内存访问性能。)

5.内存的分配和释放:

1)内存分配:

对小块内存(小于 128K),C 标准库使用 brk() 来分配,也就是通过移动堆顶的位置来分配内存,这些内存释放后并不会立刻归还系统,而是被缓存起来,这样就可以重复使用。

brk() 方式的内存:
优点:可以减少缺页异常的发生,提高内存访问效率。
缺点:由于这些内存没有归还系统,在内存工作繁忙时,频繁的内存分配和释放会造成内存碎片。

对于大块内存(大于 128K),则直接使用内存映射 mmap() 来分配,也就是在文件映射段找一块空闲内存分配出去。mmap() 方式分配的内存,会在释放时直接归还系统,所以每次 mmap 都会发生缺页异常。

mmap() 方式分配的内存:
优点:内存充足时,可以一次性分配好内存。
缺点:在内存工作繁忙时,频繁的内存分配会导致大量的缺页异常,使内核的管理负担增大。

备注:当这两种调用发生后,其实并没有真正分配内存,都只在首次访问时才分配,也就是通过缺页异常进入内核中,再由内核来分配内存。

2)内存释放:

在发现内存紧张时,系统就会通过一系列机制来回收内存,通常是下面这三种方式:

  1. 内存回收:比如使用 LRU(Least Recently Used)算法,回收最近使用最少的内存页面。

内存回收主要有两种方式:

方式一:直接内存回收:有新的大块内存分配请求,但是剩余内存不足,这个时候系统就需要回收一部分内存(比如前面提到的缓存),进而尽可能地满足新内存请求,这个过程通常被称为直接内存回收。
方式二定期回收内存:一个专门的内核线程用来定期回收内存,也就是 kswapd0。为了衡量内存的使用情况,kswapd0 定义了三个内存阈值(watermark,也称为水位),分别是页最小阈值(pages_min)、页低阈值(pages_low)和页高阈值(pages_high),剩余内存,则使用 pages_free 表示。

备注:这个页低阈值,其实可以通过内核选项 /proc/sys/vm/min_free_kbytes 来间接设置。)

kswapd0 定期扫描内存的使用情况,并根据剩余内存落在这三个阈值的空间位置,进行内存的回收操作。

75a97935d66c6edb7a485d95ef90e7fa.png

回收过程:一旦剩余内存小于页低阈值,就会触发内存的回收。

在内存资源紧张时,Linux 通过直接内存回收和定期扫描的方式,来释放文件页和匿名页,以便把内存分配给更需要的进程使用。
文件页的回收比较容易理解,直接清空缓存,或者把脏数据写回磁盘后,再释放缓存就可以了。
对不常访问的匿名页,则需要通过 Swap 换出到磁盘中,这样在下次访问的时候,再次从磁盘换入到内存中就可以了。
备注:开启 Swap 后,你可以设置 /proc/sys/vm/min_free_kbytes ,来调整系统定期回收内存的阈值,也可以设置 /proc/sys/vm/swappiness ,来调整文件页和匿名页的回收倾向。)

ii.通过Swap回收不常访问的内存:把不常用的内存通过交换分区直接写到磁盘中,通常会用到交换分区(以下简称 Swap),Swap 把这些不常访问的内存先写到磁盘中,然后释放这些内存,给其他更需要的进程使用,再次访问这些内存时,重新从磁盘读入内存就可以了。

     Swap :就是把一块磁盘空间当成内存来用,它可以把进程暂时不用的数据存储到磁盘中(这个过程称为换出),当进程访问这些内存时,再从磁盘读取这些数据到内存中(这个过程称为换入)。通常只在内存不足时,才会发生 Swap 交换,并且由于磁盘读写的速度远比内存慢,Swap 会导致严重的内存性能问题。

# 创建Swap文件
$ fallocate -l 8G /mnt/swapfile
# 修改权限只有根用户可以访问
$ chmod 600 /mnt/swapfile
# 配置Swap文件
$ mkswap /mnt/swapfile
# 开启Swap
$ swapon /mnt/swapfile
# 关闭SWAP
$ swapoff -a && swapon -a

iii.OOM(Out of Memory)杀死进程:内存紧张时系统还会通过 OOM ,直接杀掉占用大量内存的进程。

OOM(Out of Memory):是内核的一种保护机制,它监控进程的内存使用情况。
评分规则:使用 oom_score 为每个进程的内存使用情况进行评分:
1.一个进程消耗的内存越大,oom_score 就越大;
2. 一个进程运行占用的 CPU 越多,oom_score 就越小。
进程的 oom_score 越大,代表消耗的内存越多,也就越容易被 OOM 杀死,从而可以更好保护系统。

备注:管理员可以通过 /proc 文件系统,手动设置进程的 oom_adj ,从而调整进程的 oom_score。

# oom_adj 的范围是 [-17, 15],数值越大,表示进程越容易被 OOM 杀死;
# 数值越小,表示进程越不容易被 OOM 杀死,其中 -17 表示禁止 OOM。
echo -16 > /proc/$(pidof sshd)/oom_adj

6. buffer与cache:

Buffers: 是内核缓冲区用到的内存,对应的是  /proc/meminfo 中的 Buffers 值。
Buffers 是对原始磁盘块的临时存储,也就是用来缓存磁盘的数据,通常不会特别大(20MB 左右)。这样,内核就可以把分散的写集中起来,统一优化磁盘的写入,比如可以把多次小的写合并成单次大的写等等。
Cache :是内核页缓存和 Slab 用到的内存,对应的是  /proc/meminfo 中的 Cached 与 SReclaimable 之和。

Cached :从磁盘读取文件的页缓存,也就是用来缓存从文件读取的数据。下次访问这些文件数据时,就可以直接从内存中快速获取,而不需要再次访问缓慢的磁盘。

Slab:包括两部分其中的可回收部分用SReclaimable记录而不可回收部分,用 SUnreclaim 记录。

buffers:  Memory used by kernel buffers (Buffers in /proc/meminfo)


cache: Memory used by the page cache and slabs (Cached and SReclaimable in /proc/meminfo)
buff/cache: Sum of buffers and cache 


Buffers %lu
    Relatively temporary storage for raw disk blocks that shouldn't get tremendously large (20MB or so).
Cached %lu
   In-memory cache for files read from the disk (the page cache).  Doesn't include SwapCached.
...
 %lu (since Linux 2.6.19)
    Part of Slab, that might be reclaimed, such as caches. 
SUnreclaim %lu (since Linux 2.6.19)
    Part of Slab, that cannot be reclaimed on memory pressure.

Cache 和Buffer区别:

Buffer 是对磁盘数据的缓存,而 Cache 是文件数据的缓存,它们既会用在读请求中,也会用在写请求中。

Buffer 既可以用作“将要写入磁盘数据的缓存”,也可以用作“从磁盘读取数据的缓存”。Cache 既可以用作“从文件读取数据的页缓存”,也可以用作“写文件的页缓存”。

理论上,一个文件读首先到Block Buffer, 然后到Page Cache。有了文件系统才有了Page Cache,在老的Linux上这两个Cache是分开的。那这样对于文件数据,会被Cache两次。这种方案虽然简单,但低效,后期Linux把这两个Cache统一了。对于文件,Page Cache指向Block Buffer,对于非文件则是Block Buffer。这样就如文件实验的结果,文件操作,只影响Page Cache,Raw操作,则只影响Buffer。比如一此VM虚拟机,则会越过File System,只接操作 Disk, 常说的Direct IO.

备注:磁盘是一个块设备,可以划分为不同的分区;在分区之上再创建文件系统,挂载到某个目录,之后才可以在这个目录中读写文件。在读写普通文件时,会经过文件系统,由文件系统负责与磁盘交互;而读写磁盘或者分区时,就会跳过文件系统,也就是所谓的“裸I/O“。这两种读写方式所使用的缓存是不同的,也就是文中所讲的 Cache 和 Buffer 区别。)

缓存的命中率:所谓缓存命中率,是指直接通过缓存获取数据的请求次数,占所有数据请求次数的百分比。命中率越高,表示使用缓存带来的收益越高,应用程序的性能也就越好。

二、工具和命令介绍

1.free:

# Mem:物理内存,Swap:交换分区
# total: 内存的总大小;
# used:是已使用内存的大小,包含了共享内存; 
# free:未使用内存的大小
# shared:是共享内存的大小
# buff:buffer的大小
# cache: cache的大小
# available: 新进程可用内存的大小
$ free
              total        used        free      shared  buff/cache   available
Mem:        8169348      263524     6875352         668     1030472     7611064
Swap:             0           0           0

2.top

# 按下M切换到内存排序
$ top
...
KiB Mem :  8169348 total,  6871440 free,   267096 used,  1030812 buff/cache
KiB Swap:        0 total,        0 free,        0 used.  7607492 avail Mem


# VIRT: 是进程虚拟内存的大小,只要是进程申请过的内存,即便还没有真正分配物理内存,也会计算在内
# RES: 是常驻内存的大小,也就是进程实际使用的物理内存大小,但不包括 Swap 和共享内存。
# SHR: 是共享内存的大小,比如与其他进程共同使用的共享内存、加载的动态链接库以及程序的代码段等。
# MEM: 是进程使用物理内存占系统总内存的百分比。
  PID USER      PR  NI    VIRT    RES    SHR S  %CPU %MEM     TIME+ COMMAND
  430 root      19  -1  122360  35588  23748 S   0.0  0.4   0:32.17 systemd-journal
 1075 root      20   0  771860  22744  11368 S   0.0  0.3   0:38.89 snapd
...

3.vmstat

# 每隔1秒输出1组数据
# bi 和 bo 则分别表示块设备读取和写入的大小,单位为块 / 秒。
# 因为 Linux 中块的大小是 1KB,所以这个单位也就等价于 KB/s。
$ vmstat 1
procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----
r  b   swpd   free   buff  cache   si   so    bi    bo   in   cs us sy id wa st
0  0      0 7743608   1112  92168    0    0     0     0   52  152  0  1 100  0  0
 0  0      0 7743608   1112  92168    0    0     0     0   36   92  0  0 100  0  0

内存的 free 列在不停的变化,并且是下降趋势;而 buffer 和 cache 基本保持不变。未使用内存在逐渐减小,而 buffer 和 cache 基本不变,这说明,系统中使用的内存一直在升高,但这并不能说明有内存泄漏,因为应用程序运行中需要的内存也可能会增大。

4.cachestat /cachetop

cachestat 提供了整个操作系统缓存的读写命中情况。

cachetop 提供了每个进程的缓存命中情况。

# TOTAL :表示总的 I/O 次数
# MISSES ,表示缓存未命中的次数
# HITS ,表示缓存命中的次数;
# DIRTIES, 表示新增到缓存中的脏页数;
# BUFFERS_MB 表示 Buffers 的大小,以 MB 为单位;
# CACHED_MB 表示 Cache 的大小,以 MB 为单位。
$ cachestat 1 3
   TOTAL   MISSES     HITS  DIRTIES   BUFFERS_MB  CACHED_MB
       2        0        2        1           17        279
       2        0        2        1           17        279
       2        0        2        1           17        279
 # READ_HIT 和 WRITE_HIT ,分别表示读和写的缓存命中率
$ cachetop
11:58:50 Buffers MB: 258 / Cached MB: 347 / Sort: HITS / Order: ascending
PID      UID      CMD              HITS     MISSES   DIRTIES  READ_HIT%  WRITE_HIT%
   13029 root     python                  1        0        0     100.0%       0.0%

5. pcstat

$ pcstat /bin/ls
+---------+----------------+------------+-----------+---------+
| Name    | Size (bytes)   | Pages      | Cached    | Percent |
|---------+----------------+------------+-----------+---------|
| /bin/ls | 133792         | 33         | 0         | 000.000 |
+---------+----------------+------------+-----------+---------+

6.strace // 观察系统调用

# strace -p $(pgrep app)
strace: Process 4988 attached
restart_syscall(<\.\.\. resuming interrupted nanosleep \.\.\.>) = 0
openat(AT_FDCWD, "/dev/sdb1", O_RDONLY|O_DIRECT) = 4
mmap(NULL, 33558528, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f448d240000
read(4, "8vq\213\314\264u\373\4\336K\224\25@\371\1\252\2\262\252q\221\n0\30\225bD\252\266@J"\.\.\., 33554432) = 33554432
write(1, "Time used: 0.948897 s to read 33"\.\.\., 45) = 45
close(4)                                = 0

从 strace 的结果可以看到,案例应用调用了 openat 来打开磁盘分区 /dev/sdb1,并且传入的参数为 O_RDONLY|O_DIRECT(中间的竖线表示或)。O_RDONLY 表示以只读方式打开,而 O_DIRECT 则表示以直接读取的方式打开,这会绕过系统的缓存。

7.memleak

memleak 可以跟踪系统或指定进程的内存分配、释放请求,然后定期输出一个未释放内存和相应调用栈的汇总情况(默认 5 秒)。

8.sar

#kbcommit,表示当前系统负载需要的内存。它实际上是为了保证系统内存不溢出,对需要内存的估计值。
# commit,就是这个值相对总内存的百分比。
# kbactive,表示活跃内存,也就是最近使用过的内存,一般不会被系统回收。
# kbinact,表示非活跃内存,也就是不常访问的内存,有可能会被系统回收。
# kbmemfree:剩余内存
# 间隔1秒输出一组数据
# -r表示显示内存使用情况,-S表示显示Swap使用情况
$ sar -r -S 1
04:39:56    kbmemfree   kbavail kbmemused  %memused kbbuffers  kbcached  kbcommit   %commit  kbactive   kbinact   kbdirty
04:39:57      6249676   6839824   1919632     23.50    740512     67316   1691736     10.22    815156    841868         4


04:39:56    kbswpfree kbswpused  %swpused  kbswpcad   %swpcad
04:39:57      8388604         0      0.00         0      0.00


04:39:57    kbmemfree   kbavail kbmemused  %memused kbbuffers  kbcached  kbcommit   %commit  kbactive   kbinact   kbdirty
04:39:58      6184472   6807064   1984836     24.30    772768     67380   1691736     10.22    847932    874224        20


04:39:57    kbswpfree kbswpused  %swpused  kbswpcad   %swpcad
04:39:58      8388604         0      0.00         0      0.00
…
04:44:06    kbmemfree   kbavail kbmemused  %memused kbbuffers  kbcached  kbcommit   %commit  kbactive   kbinact   kbdirty
04:44:07       152780   6525716   8016528     98.13   6530440     51316   1691736     10.22    867124   6869332         0


04:44:06    kbswpfree kbswpused  %swpused  kbswpcad   %swpcad
04:44:07      8384508      4096      0.05        52      1.27

三、

1. 分析内存的性能瓶颈,可以按照下面的指标依次判断:

首先,是系统内存使用情况,比如已用内存、剩余内存、共享内存、可用内存、缓存和缓冲区的用量等。
其次,是进程内存使用情况,比如进程的虚拟内存、常驻内存、共享内存以及 Swap 内存、缺页异常等。
再次,就是 Swap 的使用情况,比如 Swap 的已用空间、剩余空间、换入速度和换出速度等。

131c01399f2f31727c8289cf56141f89.png

2.按照下面的几个步骤进行依次分析:

首先,用 free 和 top,查看系统整体的内存使用情况。
接着,用 vmstat 和 pidstat,查看一段时间的趋势,从而判断出内存问题的类型。
最后进行详细分析,比如内存分配分析、缓存 / 缓冲区分析、具体进程的内存使用分析等。

bd8109bb7189f1d5cfd31e4797148f0e.png

参考资料:

《Operating System Concepts》

https://blog.holbertonschool.com/hack-the-virtual-memory-malloc-the-heap-the-program-breakLinux 性能优化实战

  • 0
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: Android 性能优化实战 PDF 是一本介绍如何优化 Android 应用性能的电子书。该书通过实践经验和案例分析,向读者提供了一系列提高应用性能的实用技巧和方法。 该书首先介绍了性能优化的基本概念,例如应用启动时间、内存管理、网络请求和绘制性能等方面。之后,通过对真实应用情景的分析,详细介绍了一些常见的性能问题,并给出了相应的解决方案。 对于应用启动时间的优化,该书提供了一些切实可行的建议。例如合理使用启动模式、使用冷启动的启动动画、减少启动时的初始化操作等。这些方法可以明显减少应用的启动时间,提升用户体验。 在内存管理方面,该书介绍了如何减少内存泄漏和优化内存占用。通过使用合适的数据结构、避免频繁的对象创建和销毁、注意资源的主动释放等方法,可以有效地减少内存开销,提高应用的稳定性和响应速度。 对于网络请求的优化,该书提供了一些网络请求的最佳实践。例如使用合适的网络请求库、优化请求的并发性、合理使用缓存等。这些方法可以减少网络请求的时间,提高数据加载速度,提升用户体验。 对于绘制性能的优化,该书介绍了如何优化布局和渲染过程,提高界面的流畅度。例如使用合适的布局容器、避免过度绘制、使用硬件加速等。这些方法可以减少界面卡顿和掉帧现象,提升应用的视觉效果。 总而言之,Android 性能优化实战 PDF 提供了一些宝贵的经验和方法,帮助开发者优化应用的性能,提升用户体验。不仅可以帮助开发者理解性能优化的基本原理,还给出了一些具体的解决方案,对于 Android 应用的开发和优化都非常有帮助。 ### 回答2: 《Android性能优化实战》是一本针对Android开发者的重要参考书籍,旨在帮助开发者提升应用性能,提高用户体验。该书内容包含以下几个方面: 1. 性能分析工具:介绍了常用的性能分析工具,如Android Profiler、TraceView等。通过使用这些工具,开发者可以监测应用在不同场景下的性能情况,例如CPU占用、内存使用、网络请求等,从而快速定位并解决性能问题。 2. UI性能优化:详细介绍了Android UI渲染原理以及优化方法。通过合理使用View的绘制流程、减少UI层级和View的复杂度、适当使用缓存等方法,可以提升UI渲染的速度,提高应用的响应性。 3. 内存优化:介绍了内存泄漏的原因及排查方法,提供了避免内存泄漏的最佳实践。此外,还介绍了内存管理机制以及如何合理地使用内存,包括优化Bitmap的加载、使用SparseArray替代HashMap等。 4. 网络优化:讨论了网络请求中存在的性能问题,并提供了相应的解决方法。例如,合理使用缓存技术、减少网络请求的频率、使用多线程进行并发请求等。 通过学习《Android性能优化实战》,开发者可以更全面地了解和掌握Android性能优化的方法与技巧,并能够针对具体场景中的性能问题进行准确的分析和解决。这对于提高应用的性能和用户体验具有重要意义。 ### 回答3: 《Android性能优化实战》是一本介绍如何提升Android应用性能的实战指南,旨在帮助开发人员通过一系列优化技巧和方法,使应用在运行时更加稳定和高效。 该书首先介绍了性能优化的基本概念和原则,包括优化目标、优化思路等。然后详细讲解了Android系统的性能优化核心内容,如内存优化、CPU优化、网络优化等。其中涉及到了一些基本的工具和技术,如内存泄漏的检测与优化、多线程优化、数据缓存等。同时,该书还提供了一些优化实例,通过示例展示了具体的优化方法和效果。 对于开发人员来说,优化应用的性能是一个非常重要的任务,能够提高用户的体验度和应用的竞争力。《Android性能优化实战》通过详细的指导和实例,帮助读者快速理解和掌握性能优化的基本方法和技巧,同时提供了大量实战经验和案例,让读者能够更加深入地了解优化的过程和效果。 总而言之,该书是一本非常实用的Android性能优化指南,对于开发人员来说是一本必备的参考书籍。通过阅读该书,开发人员能够系统地学习和掌握Android应用的性能优化方法,提高应用的性能和质量。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值