写点什么

【YashanDB 知识库】virt 虚拟内存远大于 res 内存问题分析

作者:YashanDB
  • 2024-07-17
    广东
  • 本文字数:2761 字

    阅读完需:约 9 分钟

YASDB 内存占用简介

参数配置:

默认参数配置:DBMS_PARAM 高级包生成配置参数


数据库内存配置,使用默认参数步骤:


1、DBMS_PARAM.OPTIMIZE(); //生成默认参数,使用总内存的 80%


2、SELECT DBMS_PARAM.SHOW_RECOMMEND() FROM dual; //查看生成的参数


3、EXEC DBMS_PARAM.APPLY_RECOMMEND(); //应用参数,需要重启 yasdb


文档路径:YashanDB Doc (yasdb.com)

内存占用计算参数:

DATA_BUFFER_SIZE VM_BUFFER_SIZE SHARE_POOL_SIZE LARGE_POOL_SIZE SCOL_DATA_BUFFER_SIZE COLUMNAR_VM_BUFFER_SIZE 主要是上面这些参数配置内存的总和 + 256M(其他小块内存)

问题:VIRT 超过 RES 十几个 G

如下图所示,yasdb 满负荷运行后,0 任务跑的情况下,virt 内存:31.2g,实际内存:21.7g,虚拟内存比实际内存大了 10g 左右,虚拟内存远大于实际使用内存。


YASDB 内存使用情况

yasdb 参数配置,如下图:



按照 yasdb 主要内存占用计算:


DATA_BUFFER_SIZE(18306M) + VM_BUFFER_SIZE(2382M) + SHARE_POOL_SIZE(512M) + LARGE_POOL_SIZE(1G) + SCOL_DATA_BUFFER_SIZE(128M) + COLUMNAR_VM_BUFFER_SIZE (128M) = 22480M(21.95g)


RES 21.7g 的内存使用和 yasdb 内存总占用数相符合。

VIRT 内存为什么远远大于 RES

VIRT:SWAP+RES(虚拟内存大小,包括进程使用的库、代码、数据等,如果申请 100M,则增加 100M 大小)


RES:进程使用的,未被换出的物理内存(包括共享内存大小,如果申请 100M,使用 10M,则实际为 10M)

mmap 查看 yasdb 使用情况

如下图:


strace -f -e "brk,mmap,munmap" -p 2051595



yasdb 没有使用 mmap 等映射接口。

pmap 工具,查看进程内存映射信息

pmap 命令用于报告进程的内存映射关系(查看进程的内存映像信息)


选项:


  • -x, --extended:显示扩展格式

  • -d, --device:显示设备格式

  • -X:显示比 -x 选项更多的详细信息。注意:格式根据 /proc/PID/smaps 更改

  • -p, --show-path:在映射列中显示文件的完整路径

  • -h, --help:显示帮助信息并退出

  • -V, --version:显示版本信息并退出


扩展格式和设备格式域:


  • Address: start address of map 映像起始地址

  • Kbytes: size of map in kilobytes 映像大小

  • RSS: resident set size in kilobytes 驻留集大小

  • Dirty: dirty pages (both shared and private) in kilobytes 脏页大小

  • Mode: permissions on map 映像权限: r=read, w=write, x=execute, s=shared, p=private (copy on write)

  • Mapping: file backing the map , or ‘[ anon ]’ for allocated memory, or ‘[ stack ]’ for the program stack. 映像支持文件,[anon]为已分配内存 [stack]为程序堆栈

  • Offset: offset into the file 文件偏移

  • Device: device name (major:minor) 设备名


pmap -d ${pid}

glibc 分配内存机制

  • 导致这种问题的原因是使用 glibc 的 Arena 内存池分配了大量的虚拟内存。

  • glibc 大于 2.1.1 版本时,在 glibc 分配内存的时候,大内存从从中央分配区分配,小内存则在线程创建时,从缓存区分配。

  • 为了解决分配内存的性能的问题,就引入了这个叫做 arena 的 memory pool。而恰好,在 64bit 系统下面,它的缺省配置为 64M。

  • 一个进程可以最多有 cores * 8 个 arena,假如服务器是 4 核的,那么最多有 4 * 8=32 个 arena,也就是 32 * 64 = 2048M 内存


参考文档:

https://www.cnblogs.com/sky-heaven/p/6273305.html

https://access.redhat.com/documentation/enus/red_hat_enterprise_linux/6/html/6.0_release_notes/compiler

glibc 版本:

内核数 cores:

MALLOC_ARENA_MAX:

环境变量,控制 arena 内存池个数。


查看状态:


cat /proc/pid/status

测试 arena 内存池代码程序:

#include <string.h>#include <stdlib.h>#include <stdio.h>#include <stdbool.h>#include <pthread.h> bool kill_th = false; void *th_func(void *arg){
int s = 0; //int size = 1024*1024*139; int size2 = 1024; //char *pbuf; while(!kill_th){ if(s == 0){ //pbuf = (char *)malloc(size); // free(pbuf); char *pbuf2 = (char *)malloc(size2); free(pbuf2); s = 1; } sleep(1); } //free(pbuf);} void create_thread(){ pthread_t tid; int nums = 256; for(int i = 0; i< nums;i++){ pthread_create(&tid,NULL,th_func,NULL); pthread_detach(tid); } while(getchar()){ kill_th = true; getchar(); break; } return ;} int main(int argc,const char **argv) { create_thread(); return 0;}
复制代码


超过 64M 大小的空间不从 arena 内存池申请。


编译:gcc test.c -lpthread

ulimit -a:


运行测试程序 : ./a.out:

默认 MALLOC_ARENA_MAX

arena 池默认个数:16 * 8 = 128


默认,cat /proc/$pid/environ,如下图:



该文件没有 MALLOC_ARENA_MAX 参数


● 256 个线程


内存使用情况:



pmap -p pid 信息如下:pmap_malloc_arena_max_normal.txt


arena 内存池:127 *(65344K + 192K)


线程栈空间:256 * 8192K


8192K * 256 + 64M*127 = 10176M,


再加上其它小空间,符合虚拟内存使用情况。


● 256 个线程退出


内存使用情况:



pmap 映射进程信息:pmap_maloc_arena_normal_thread_exit.txt


arena 内存池:127 *(65344K + 192K)= 8128M


栈空间:仅剩主线程空间


可以看出,线程退出后,arena 空间并没有释放。

export MALLOC_ARENA_MAX = 4

MALLOC_ARENA_MAX 值: cat /proc/$pid/environ:



可以看到参数 MALLOC_ARENA_MAX 配置为 4


● 256 个线程


top 信息:



pamp -p pid 信息: pmap_malloc_arena_max=4.txt


线程栈空间:256*8M


arena 内存池:3 * (65344K + 192k)


● 256 线程退出


top 信息:



pamp -p pid 信息,如下图:



arena 内存池:3 * (65344K + 192k)

yasdb 的内存映射情况

参数


WORK_AREA_STACK_SIZE 2M


WORK_AREA_HEAP_SIZE 512K


得知栈空间大小为 2M,堆空间为 512K


分析 pmap 信息


pmap -p {pid}(yasdb),文件:pmap_yasdb.txt


arena 内存池:97 * (65024K+512k) + 5 * (65216K+320k) + 1*(64576K +960K ) + 1 * (53824K + 11712K) + 23 *(64896K + 640K) = 127 * 64M = 8128 M


栈空间:390 * 1984K + 9 * 8192K = 827.625M


其它:lib 库映射空间等


总共相加,大概可以得出与多出的虚拟内存相等。


配置线程参数一些接口


每个线程都会分配一个栈(默认 8M,可配置)和一个堆空间。


pthread_attr_t attr;


pthread_attr_init(&attr);


pthread_attr_setstacksize(&attr, stackSize); //配置线程属性->栈空间大小


prctl(PR_SET_NAME, name); //设置线程名


pthread_detach(tid); //线程退出后,自动释放空间。如果没有配置这个需要主动释放 pthread_exit。不然会一直占用虚拟内存


ulimit -a 可看堆栈默认值,也可配置堆栈默认值。

用户头像

YashanDB

关注

全自研国产新型大数据管理系统 2022-02-15 加入

还未添加个人简介

评论

发布
暂无评论
【YashanDB知识库】virt虚拟内存远大于res内存问题分析_yashandb_YashanDB_InfoQ写作社区