背景
上周五培训时, 达梦数据库老师讲到了 MALLOC_ARENA_MAX的参数
我记得自己很早之前曾经看过这个参数
但是当时一直不是很理解.
培训过程中他们建议是将参数修改"1"
我感觉这个参数对自己理解内存有帮助,所以想多学习一下.
然后进行一下简单的总结.
MALLOC_ARENA_MAX 的理解
glibc 从 2.11 左右开始的版本对多线程做了很多增强.
最大的增强是: 允许在多线程模式下可以开启多个ARENA
其中ARENA的含义 是连续的虚拟内存段地址范围.
32位系统默认是1MB, 64位系统默认是 64MB.
如果不设置参数的情况下:
MALLOC_ARENA_MAX 的默认值是 CPU的Core数目*8
他这样的好处是, 多线程在分配内存时, 可以有更多的ARENA区域来使用
因为在ARENA内分配内存是需要加锁和解锁的.
降低加锁解锁的过程能够极大的提升内存分配效率
这也就是为啥 栈内分配要比对内分配要快非常多的原因.
虽然数量多的ARENA 可以提升内存分配效率.但是过多的_ARENA_也存在风险
一方面会导致 进程的virt 存在过量的问题
另一方面是 如果 java对堆外内存的回收存在问题,会导致缓慢的内存泄露
导致因为堆外内存泄露引起的OOM宕机.
建议不要设置的太大, 避免出现问题.
查看Java进程相关的ARENA大概数量
for i in `ps -ef |grep java |grep caf |awk '{print $2}' ` ;\
do echo -n "该进程: $i 的ARENA数大概为: " ; pmap -x $i \
|awk '{print $2}' |grep ^6 \
|grep -o '[[:digit:]]\{5\}' |wc -l ; \
done
查看分布情况
该进程: 187121 的ARENA数大概为: 125
该进程: 190445 的ARENA数大概为: 242
该进程: 190448 的ARENA数大概为: 229
该进程: 190455 的ARENA数大概为: 237
该进程: 190456 的ARENA数大概为: 235
该进程: 190459 的ARENA数大概为: 239
该进程: 190460 的ARENA数大概为: 223
该进程: 190461 的ARENA数大概为: 246
该进程: 190462 的ARENA数大概为: 243
该进程: 190464 的ARENA数大概为: 236
该进程: 190474 的ARENA数大概为: 224
该进程: 190486 的ARENA数大概为: 242
查看java线程的最小数量-pmap的其他应用之一
for i in `ps -ef |grep java |grep caf |awk '{print $2}' ` ;\
do echo -n "该进程: $i 的线程数至少为: " ; pmap -x $i \
|awk '{print $2}' |grep ^1 \
|grep -o '[[:digit:]]\{4\}' |wc -l ; \
done
查看java线程映射文件的大小-pmap的其他应用之二
for i in `ps -ef |grep java |grep -v nacos |grep -v rtf |grep -v grep \
|awk '{print $2}'` ; do echo -n $i ; echo -n ": 该进程的jar以及so占用内存情况(KB): " ;\
pmap -X $i |awk '$5>0 {print $7}' |grep -v '=' | awk '{sum +=$1} END {print sum}' ;\
done
微服务的影响
glibc 其实做了很多优化
有部分blog 提示说:
如果一次申请1kb的内存, 当前没有可用的ARENA并且还可以申请ARENA的话
那么glibc会给进程一个新的ARENA 的区域进行 内存分配.
但是如果一次申请1MB的内存, 那么进程会直接将内存分配到Main的ARENA区域上面.
避免新开启一个ARENA导致的内存损耗.
我这边发现微服务情况下的ARENA区域特别多.
怀疑原因是 rpc通信时要快速形成很多线程进行交互
此时 每次申请的线程内存用量都很小, 所以glibc习惯性的给一个新的ARENA
导致ARENA的越来越多, 最终导致virt 内存非常多.
可以通过top 能够看到
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
187121 root 20 0 28.3g 10.9g 12576 S 92.7 5.8 840:41.10 java
190445 root 20 0 36.5g 10.1g 10100 S 3.6 5.4 61:09.85 java
190486 root 20 0 28.0g 7.7g 12936 S 38.9 4.1 238:09.34 java
190474 root 20 0 27.4g 7.0g 7984 S 2.0 3.7 57:22.88 java
190455 root 20 0 27.6g 6.6g 13024 S 7.9 3.5 50:04.96 java
190461 root 20 0 28.1g 6.3g 16412 S 3.3 3.3 62:53.61 java
会发现 virt 比 res 要高很多.
过多ARENA的理解
glibc的为了实现多线程快速的内存分配才有除了main ARENA之外的ARENA.
但是主分区可以支持 sbrk和mmap的内存申请使用. 从属分区只能使用mmap进行分配.
每个线程在运行时,先去检查自己所在CPU有没有空闲的ARENA, 如果有的话直接使用.
如果没有的话再创建新的一个新的ARENA进行内存配置, 只要总数不超过Core*8就可以.
这种情况下. 在多线程时能够极大的提高内存分配的效率,
避免无效的对内存分配区域的等待
这种情况下过多的ARENA 的确能够提高性能.
但是过多的ARENA 对内存管理会带来挺大的损耗和碎片风险
会导致 virt 的区域内存大于 rss的内存很大.
ARENA的内存都会进入 virt 区域. 但是实际commit的内存才是rss的.
文章转载自济南小老虎,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。




