前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >原创|X侦探所事件簿|内存到哪里去了

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

作者头像
腾讯数据库技术
发布2022-03-30 12:40:12
5220
发布2022-03-30 12:40:12
举报

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

「第一部分 前言」

我们都知道,程序的运行离不开内存。很多人都有这种直接朴素的想法,内存越大程序的运行速度越快。对于数据库来说,如果数据都能加载到内存中,不需要从磁盘读取,那速度肯定是杠杠的。但是,对于现在的应用来说,几十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

腾讯数据库技术团队对内支持QQ空间、微信红包、腾讯广告、腾讯音乐、腾讯新闻等公司自研业务,对外在腾讯云上依托于CBS+CFS的底座,支持TencentDB相关产品,如TDSQL-C(原CynosDB)、TencentDB for MySQL(CDB)、CTSDB、MongoDB、CES等。腾讯数据库技术团队专注于持续优化数据库内核和架构能力,提升数据库性能和稳定性,为腾讯自研业务和腾讯云客户提供“省心、放心”的数据库服务。此公众号旨在和广大数据库技术爱好者一起推广和分享数据库领域专业知识,希望对大家有所帮助。

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2022-03-29,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 腾讯数据库技术 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 「第一部分 前言」
  • 1.1 运维中的内存
    • 1.3 /proc/[pid]/status
      • 1.4 size
        • 1.5 /proc/[pid]/maps
        • 「第二部分 代码看内存」
          • 2.1 测试1
            • 2.2 测试2
              • 结论1
            • 2.3 测试3
              • 未初始化
              • 初始化
          • 「第三部分 结束语」
          • 「第四部分 参考文献」
          相关产品与服务
          云数据库 SQL Server
          腾讯云数据库 SQL Server (TencentDB for SQL Server)是业界最常用的商用数据库之一,对基于 Windows 架构的应用程序具有完美的支持。TencentDB for SQL Server 拥有微软正版授权,可持续为用户提供最新的功能,避免未授权使用软件的风险。具有即开即用、稳定可靠、安全运行、弹性扩缩等特点。
          领券
          问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档