首页 > 数据库 >MySQL内存分配详解

MySQL内存分配详解

时间:2023-08-02 17:26:22浏览次数:40  
标签:详解 内存 key MySQL pfx my ptr size

InnoDB存储引擎层基础内存分配

如果在编译MySQL的时候不开启FPS的监控,InnoDB对动态内存(heap)的分配和释放使用基础的new、delete、malloc、free等。

默认InnoDB对内存的分配和回收会添加FPS的监控模块。InnoDB对动态内存(heap)的分配和回收使用封装后的函数,主要在ut_allocator类中实现,具体内存的分配和回收方法有以下几种:

  • 单块内存分配函数:allocate
  • 单块内存回收函数:deallocate
  • 内存重新分配函数:reallocate
  • Array对象内存分配函数:new_array
  • Array对象内存回收函数:delete_array
  • 大块内存分配函数:allocate_large
  • 大块内存回收函数:deallocate_large
  • 内存分配跟踪函数:allocate_trace
  • 内存回收跟踪函数:deallocate_trace

其中还需要两个重要的数据对象ut_new_pfx_t和PSI_memory_key。

PSI_memory_key指的是Performance schema key,标识了调用PFS接口进行内存分配或回收监控时的事件名称(即event_name),以下为一些非默认的事件名称,如果没有指定事件名称,则使用默认的event_name(比如memory/innodb/dict0stats、memory/innodb/std):

ut_new_pfx_t结构中包含了三个属性,分别是:

  • PSI_memory_key m_key
  • struct PSI_thread *m_owner:内存操作所属的线程
  • size_t m_size:分配内存的大小(以字节为单位),包括pfx结构本身占用的内存

下面具体看一下“单块内存分配函数:allocate”的实现逻辑,allocate函数主要用于容器内部对象的内存分配,包括上面讲的“Array对象内存分配函数:new_array”也是调用allocate进行的内存分配。具体代码如下(省略部分代码,只截取关键语句):

  pointer allocate(size_type n_elements, const_pointer hint = nullptr,
                   PSI_memory_key key = PSI_NOT_INSTRUMENTED,
                   bool set_to_zero = false, bool throw_on_error = true) {
void *ptr;
// 统计待分配对象占用内存大小
size_t total_bytes = n_elements * sizeof(T);
// 总共需要的内存大小(包括pfx对象)
total_bytes += sizeof(ut_new_pfx_t);
// 进行内存分配,如果分配失败,进行重试操作
    for (size_t retries = 1;; retries++) {
      if (set_to_zero) {
        ptr = calloc(1, total_bytes);
      } else {
        // 调用标准的malloc函数进行内存分配
        ptr = malloc(total_bytes);
      }
      if (ptr != nullptr || retries >= alloc_max_retries) {
        break;
      }
      std::this_thread::sleep_for(std::chrono::seconds(1));
}

// 如果达到最大失败次数,则抛出异常,相关代码省略

// 初始化pfx对象信息
ut_new_pfx_t *pfx = static_cast<ut_new_pfx_t *>(ptr);
// 跟踪内存的分配
    allocate_trace(total_bytes, key, pfx);
    return (reinterpret_cast<pointer>(pfx + 1));
  }

下面具体看一下“单块内存回收函数:deallocate”的具体实现:

  void deallocate(pointer ptr, size_type n_elements = 0) {
    // 内存回收时跟踪内存的分配并调用标准free函数进行内存回收
    ut_new_pfx_t *pfx = reinterpret_cast<ut_new_pfx_t *>(ptr) - 1;
    deallocate_trace(pfx);
    free(pfx);
  }

下面看一下“大块内存分配函数:allocate_large”(比如用于Buffer Pool内存的resize)的具体实现(省略部分代码):

  pointer allocate_large(size_type n_elements, ut_new_pfx_t *pfx) {
ulint n_bytes = n_elements * sizeof(T);
/* 
large page的内存分配主要是通过os_mem_alloc_large
函数进行mmap系统调用直接分配内存,并进行内存跟踪
    */
pointer ptr = reinterpret_cast<pointer>(os_mem_alloc_large(&n_bytes));
    if (ptr != nullptr) {
      allocate_trace(n_bytes, PSI_NOT_INSTRUMENTED, pfx);
    }
    return (ptr);
  }

而“大块内存回收函数:deallocate_large”则主要是通过os_mem_free_large函数进行munmap系统调用进行内存释放并进行内存跟踪监控:

  void deallocate_large(pointer ptr, const ut_new_pfx_t *pfx) {
    deallocate_trace(pfx);
    os_mem_free_large(ptr, pfx->m_size);
  }

内存的分配或回收的跟踪监控主要是通过调用PSI监控接口来实现。

内存分配监控跟踪:

  void allocate_trace(size_t size, PSI_memory_key key, ut_new_pfx_t *pfx) {
    if (m_key != PSI_NOT_INSTRUMENTED) {
      key = m_key;
    }
    pfx->m_key = PSI_MEMORY_CALL(memory_alloc)(key, size, &pfx->m_owner);
    pfx->m_size = size;
  }

内存回收监控跟踪:

  void deallocate_trace(const ut_new_pfx_t *pfx) {
    PSI_MEMORY_CALL(memory_free)(pfx->m_key, pfx->m_size, pfx->m_owner);
  }

而实际进行内存分配或回收时主要是通过一些宏或容器对象来进行的,比如UT_NEW(expression, key)、UT_NEW_ARRAY(type, num, key)、UT_DELETE_ARRAY(ptr)、std::vector<t, ut_allocator<t> > v(ut_allocator<t>(key))、ut_malloc、ut_free等,这些宏在进行内存分配的时候都指定了PSI_memory_key,还有一些宏使用默认的key,如UT_NEW_NOKEY(expression)、UT_NEW_ARRAY_NOKEY(type, num)、 std::vector<t, ut_allocator<t> > v等。

Server层基础内存分配

Server层内存的基础分配和回收函数主要是以下几种:

下面具体看一下my_malloc的代码实现:

/* 
my_malloc函数主要是通过调用my_raw_malloc函数来进行内存分配;并直接调用PSI接口进行内存跟踪
*/
void *my_malloc(PSI_memory_key key, size_t size, myf flags) {
  my_memory_header *mh;
  size_t raw_size;
  raw_size = HEADER_SIZE + size;
  mh = (my_memory_header *)my_raw_malloc(raw_size, flags);
  if (likely(mh != nullptr)) {
    void *user_ptr;
    mh->m_magic = MAGIC;
    mh->m_size = size;
    mh->m_key = PSI_MEMORY_CALL(memory_alloc)(key, size, &mh->m_owner);
    user_ptr = HEADER_TO_USER(mh);
    MEM_MALLOCLIKE_BLOCK(user_ptr, size, 0, (flags & MY_ZEROFILL));
    return user_ptr;
  }
  return nullptr;
}
// 而my_raw_malloc函数的实现主要是通过malloc标准函数进行内存分配(省略部分代码)
static void *my_raw_malloc(size_t size, myf my_flags) {
  void *point;
  /* Safety */
  if (!size) size = 1;
  if (my_flags & MY_ZEROFILL)
    point = calloc(size, 1);
  else
    point = malloc(size);
    point = nullptr;
  });
  return (point);
}

下面看一下my_free的代码实现:

// my_free的实现逻辑主要是通过调用my_raw_free函数实现,并直接调用PSI接口进行内存跟踪
void my_free(void *ptr) {
  my_memory_header *mh;
  if (ptr == nullptr) return;
  mh = USER_TO_HEADER(ptr);
  assert(mh->m_magic == MAGIC);
  PSI_MEMORY_CALL(memory_free)(mh->m_key, mh->m_size, mh->m_owner);
  /* Catch double free */
  mh->m_magic = 0xDEAD;
  MEM_FREELIKE_BLOCK(ptr, 0);
  my_raw_free(mh);
}

// my_raw_free则使用标准free函数进行内存释放
static void my_raw_free(void *ptr) {
  free(ptr);
}

从以上内存分配相关代码可知:内存分配的标准函数主要涉及new、malloc、mmap。

TRANSLATE with x English
Arabic Hebrew Polish
Bulgarian Hindi Portuguese
Catalan Hmong Daw Romanian
Chinese Simplified Hungarian Russian
Chinese Traditional Indonesian Slovak
Czech Italian Slovenian
Danish Japanese Spanish
Dutch Klingon Swedish
English Korean Thai
Estonian Latvian Turkish
Finnish Lithuanian Ukrainian
French Malay Urdu
German Maltese Vietnamese
Greek Norwegian Welsh
Haitian Creole Persian  
  TRANSLATE with COPY THE URL BELOW Back EMBED THE SNIPPET BELOW IN YOUR SITE Enable collaborative features and customize widget: Bing Webmaster Portal Back

标签:详解,内存,key,MySQL,pfx,my,ptr,size
From: https://www.cnblogs.com/wagaga/p/17601216.html

相关文章

  • 基础 | 详解3D结构光如何标定
    结构光视觉的优点:非接触、信息量大、测精度高、抗干扰能力强。结构光视觉传感器参数的标定包括:摄像机参数标定、结构光平面参数标定。结构光视觉测量原理图我们不考虑镜头的畸变,将相机的成像模型简化为小孔成像模型,则特征点的图像坐标Pf与其在摄像机坐标系下的三维坐标P之......
  • jdk7 jdk8 堆内存区别
    1、堆内存划分在JDK7以及其前期的JDK版本中,堆内存通常被分为三块区域Nursery内存(young generation)、长时内存(oldgeneration)、永久内存(PermanentGenerationfor VMMatedata),显示如下图: jdk7之前堆内存不够最常见的错误就是OOM(OutOfMemoryError)栈内存溢出最常......
  • 请求头详解——Headers
    请求头定义:客户端,即浏览器通过输入url后确定等于做了一次向服务器的请求动作,在这个请求里面带有请求参数,请求参数的集合即为请求头。请求头的参数(常用的):Accept:浏览器可接收的文件类型Accept-Charset:浏览器可以接收的编码类型Accept-Encoding:浏览器可以接收的压缩编码类型Ac......
  • MySQL8.0 JSON数据类型
    (目录)JSON数据类型MySQL8.0支持原生的JSON数据类型。JSON数据类型存在以下优点自动验证存储在json列中的JSON文档,无效文档会产生错误优化存储格式。json列中的文档被转换为内部格式,可以直接对文档元素进行快速读取访问。JSON值JSON值类型在MySQL中,JSON值可以是字符串/数......
  • maven-resources-plugin详解
    核心资料来源:maven-resources-plugin详解(csdn.net)maven-resources-plugin到低在什么场景下使用?他到底有什么作用?他和pom当中配置的resources标签又有什么关联?为什么有的项目使用了该插件而有的却没有?resources当中的filtering标签到低是干什么的?maven怎么打包过滤文件?......
  • Spring内存码
    Spring内存码依然不会配环境orz,干脆直接拿以前那个java-sec-code了,springboot版本2.1.5.RELEASEspring内存码基础的有controller型和interceptor型,两个组件都可以动态添加,注入思路和以前一样,所以先看初始化的流程一、Controller型controller作用是接收特定参数,与@RequestMappi......
  • 8、MySQL数据表基本数据类型
    数字整数还有其他的数据类型,分成了几种:1、数字类型2、字符串类型3、时间类型总共可以分成这么几类,咱们的数字里面有个int,除了int还有什么呢,还有tinyint、bigint,它们都是int,它们之间的区别是什么,它是不是能表示的数据范围是不一样的。比如对于tinyint,它只能表示有符号的-128......
  • mysql添加用户并分配权限
    CREATEUSER'username'@'ip'IDENTIFIEDBY'password';GRANTprivilegesONdatabase.tableTO'username'@'ip';其中:username替换为用户名,例如“bigman”ip替换为可访问数据库的ip,例如“127.0.0.1”“192.168.1.1”,使用“%”表示不限制远程连接ippassw......
  • win 11 无法安装ensp 组件VBox(版本过老)导致AR路由器报错 40,关闭win11 内存完整性 开关
        解决办法如下:1、先关闭内存完整性 2、重新安装vbox(成功) 3、启动ensp(无40报错) ......
  • 镭拓详解塑料激光焊接机在汽车车灯塑料焊接上的应用
    编辑:镭拓激光随着激光焊接技术的快速发展与进步,激光技术应用深入到各个领域,尤其是在高精密制造业领域。比如汽车制造行业上,激光焊接技术的应用更为深刻和重要。不过很多人可能不知道是,汽车制造业使用到的激光焊接技术可不仅仅是一种。你激光焊接技术只是用在汽车车上的金属材料焊接......