内存到哪里去了

原创|X侦探所事件簿|内存到哪里去了 https://mp.weixin.qq.com/s/3ofmJhRwIZD-GV8TYxVLGw

原创|X侦探所事件簿|内存到哪里去了

提示:公众号展示代码会自动折行,建议横屏阅读

 

「第一部分 前言」

我们都知道,程序的运行离不开内存。很多人都有这种直接朴素的想法,内存越大程序的运行速度越快。对于数据库来说,如果数据都能加载到内存中,不需要从磁盘读取,那速度肯定是杠杠的。但是,对于现在的应用来说,几十GB乃至TB级别的数据,都是常见的情况,但内存多是十几GB。所以,内存就是就是珍贵的资源,要精打细算的使用,那么这次我们就探究一下和内存相关的知识。这些知识将从两个方面着手,一是操作系统方面,从该方面我们讲述内存在这个层面是怎么分布的;二是MySQL方面,从该方面我们了解在运行中MySQL的各个模块的内存申请情况。以及最后,这两方面结合看所产生的一些问题。本章,我们从操作系统来了解一下有哪些内存概念,以及通过一些实验来验证这些概念。

 

1.1 运维中的内存

大多数时候,我们是从运维角度来了解内存的,看看系统有多少内存,程序占用多少内存,这里我们通过工具以及操作系统信息,简单了解程序运行时内存使用情况。

 

1.2 TOP

查看内存的第一个方法是使用top命令来查看程序的运行情况。如下图所示

图片

 

图片中展示的PID是进程在操作系统中运行时ID编号,通过这个ID唯一标识一个进程。输出信息中,内存相关最重要的是VIRT和RES这两个指标,其中VIRT是使用的虚拟内存空间,RES是实际占用的常驻内存空间。
在这里面要解释一下虚拟内存空间和常驻内存空间。虚拟内存空间是通过mmap/malloc/new等方式,向操作系统声明要使用的空间,但是这些操作所获取的内存空间并没有被读取或者写入,此时没有使用内存条上物理空间;常驻内存空间是实际占用的内存空间,也就是通过读取或者写入内容,真正意义上占用了内存条上物理空间。

 

1.3 /proc/[pid]/status

进一步细致地查看内存的方式是通过/proc/[pid]目录下的status文件,可以看到内存的使用情况。如下图所示,通过ps获取进程的PID,并展示/proc目录下该PID的status的内容。

图片

图片

图片

 

在该文件中,更详细的展示程序运行中内存使用情况,不同内核版本输出的信息不尽相同,所以需要根据实际环境查找对应手册。其中VmSize和top中的VIRT是相同含义,VmRSS和top中的RES是相同含义;在输出的内容中的内存使用还包括了VmExe等信息。这里VmExe是程序的代码段所需要的虚拟空间,VmData是程序运行时的虚拟数据空间的大小,VmExe是程序代码段的大小,VmLib是共享库代码空间的大小。
 

1.4 size

上面引入的VmExe等概念可以通过size命令静态的了解执行文件在内存中的相关信息,如下图所示

图片


其中text是代码(也就是CPU指令)所占用的空间对应VmExe,data是初始值的全局变量空间,bss是没有初始值的全局变量的空间,这些内存空间是在程序运行启动时,就需要分配给进程的。
 
由size命令,引入代码段等概念,我们需要介绍一下Linux运行时,程序的内存空间分布,其情况如下图所示

图片

 

所有代码的代码段都是从0x400000的虚拟地址空间开始的,从这里开始,以此向上存储的是代码空间,初始化全局变量空间,未初始化全局变量的空间。特殊说明一下,在不同的版本下面虚拟内存映射可能会有些差异,需要结合特定版本的手册来准确定位这些信息。
 

1.5 /proc/[pid]/maps

更进一步的查看内存空间的使用情况需要查看/proc/[pid]/maps文件,该文件的功能是记录了内存申请的虚拟地址(如malloc等内存分配函数)。如下所示,在maps文件中有如下内容
用于加在代码段的0x400000地址起始的代码空间:

图片


主线程的堆栈空间:

图片


子线程的堆栈空间:

图片


所使用的动态链接库的地址空间:

图片


所有类似如上空间的总和,构成了程序在内存中运行的全部虚拟空间。但这里要注意,这些空间很多可能没有实际存储数据,因此不会使用到内存条上的物理空间。在Linux下,只有当读写这些虚拟内存,触发了page fault中断,才会将虚拟内存和物理内存建立映射关系。这时的物理内存将会被统计到VmRSS(status文件)和RES(top命令)。
 
如上我们从运维的角度了解到内存的一些知识,下面将会通过简单的代码,验证一下关于mmap,malloc以及thread stack 局部变量和status文件中VmRSS等信息的关系。

 

「第二部分 代码看内存」

#include <stdio.h>#include <pthread.h>#include <stdlib.h>#include <string>#include <string.h>#include <sys/mman.h>
static int stop_thread = 0;
int sleep_in_second(int n){ struct timespec time_to_sleep, time_real_sleep;
time_to_sleep.tv_sec = n; time_to_sleep.tv_nsec = 0; nanosleep(&time_to_sleep, &time_real_sleep);}
static void* thread_func(void *arg){ int temp = (int)((long long)arg); printf("thread %d\n", temp); if (temp == 1) { //thread 1 //local variables memory test const int local_size = 4*1024*1024; char buf[local_size]; memset(buf, 0, local_size);
//malloc memory test const int malloc_size(8*1024*1024); char *p = (char*)malloc(malloc_size); for (int i = 0; i < malloc_size; i++) { p[i] = (i % 26) + 'a'; } //mmap memory test; const int mmap_size = 16*1024*1024; char *p2 = nullptr; p2 = (char*)mmap(NULL, mmap_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); for (int i = 0; i < mmap_size; i++) { p2[i] = (i % 26) + 'a'; } } else { //thread 2 }
while (1) { if (stop_thread) break; sleep_in_second(1); } return 0;}
intmain(int argc, char *argv[]){ pthread_t thread; pthread_attr_t attr; pthread_attr_init(&attr); const int stack_16m = 16*1024*1024; pthread_attr_setstacksize(&attr, stack_16m);
//default 8M thread stack size pthread_create(&thread,&attr,&thread_func,(void*)1); //16M thread stack size pthread_create(&thread,NULL,&thread_func,(void*)2);
getchar(); stop_thread = 1;
return 0;}//g++ mem_test.cc -o mem_test -lrt -lpthread -std=c++11

 


2.1 测试1

没有线程及相关代码,基线测试
size命令

图片

 

status文件

图片

 

2.2 测试2

创建线程,并定义本地变量未初始化/初始化
未初始化本地变量
size命令

图片


status文件

图片


初始化本地变量
size命令

图片


status文件

图片

 

结论1

通过对该测试的VmRSS进行观察,可以得出一个结论,线程本地stack上定义的变量,如果没有初始化,则不会产生实际的空间占用。初始化后,将会占用实际的物理空间。
 

2.3 测试3

malloc和mmap分配,未初始化/初始化

未初始化

size命令

图片


status文件

图片

 

初始化

size命令

图片


status文件

图片


以上是简单的实验,来验证内存的一些情况,并未涵所有的情况,比如在在线程函数内定义int类型的变量会是什么情况?对应的maps文件中内存的分配是什么情况?这些都留给读者自己去尝试,去验证。
 

「第三部分 结束语」

内存是程序运行的核心,在MySQL的运行中内存的使用是云用户关注的重要指标之一,这涉及到购买资源是否足够。在下一篇中,我们将会从MySQL的角度介绍内存是如何被MySQL所管理的。

 

「第四部分 参考文献」

https://man7.org/linux/man-pages/man5/proc.5.html
https://linuxtools-rst.readthedocs.io/zh_CN/latest/tool/size.html

 

posted @ 2022-05-06 16:51  papering  阅读(183)  评论(0编辑  收藏  举报