【YashanDB 知识库】virt 虚拟内存远大于 res 内存问题分析
YASDB 内存占用简介
参数配置:
默认参数配置:DBMS_PARAM 高级包生成配置参数
数据库内存配置,使用默认参数步骤:
1、DBMS_PARAM.OPTIMIZE(); //生成默认参数,使用总内存的 80%
2、SELECT DBMS_PARAM.SHOW_RECOMMEND() FROM dual; //查看生成的参数
3、EXEC DBMS_PARAM.APPLY_RECOMMEND(); //应用参数,需要重启 yasdb
内存占用计算参数:
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
glibc 版本:
内核数 cores:
MALLOC_ARENA_MAX:
环境变量,控制 arena 内存池个数。
查看状态:
cat /proc/pid/status
测试 arena 内存池代码程序:
超过 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 可看堆栈默认值,也可配置堆栈默认值。
评论